Slide 1

Slide 1 text

Build Your Custom Observable/Operator Hiroshi Kurokawa (@hydrakecat)

Slide 2

Slide 2 text

Reference • The Slides
 https://speakerdeck.com/hkurokawa/operator • RxNearby
 https://github.com/hkurokawa/rxnearby

Slide 3

Slide 3 text

When you need custom Observable/Operator? // Retrofit service.users() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(users -> { adapter.setItems(users); }); // RxBinding RxView.scrollChangeEvents(listView) .subscribe(event -> { int dy = event.scrollY() - event.oldScrollY(); });

Slide 4

Slide 4 text

When you need custom Observable/Operator? // Retrofit service.users() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(users -> { adapter.setItems(users); }); // RxBinding RxView.scrollChangeEvents(listView) .subscribe(event -> { int dy = event.scrollY() - event.oldScrollY(); }); When you want to model something as a stream!

Slide 5

Slide 5 text

When you need custom Observable/Operator? // Retrofit service.users() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(users -> { adapter.setItems(users); }); // RxBinding RxView.scrollChangeEvents(listView) .subscribe(event -> { int dy = event.scrollY() - event.oldScrollY(); }); When you want to model something as a stream!

Slide 6

Slide 6 text

When you need custom Observable/Operator? // Retrofit service.users() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(users -> { adapter.setItems(users); }); // RxBinding RxView.scrollChangeEvents(listView) .subscribe(event -> { int dy = event.scrollY() - event.oldScrollY(); }); When you want to model something as a stream!

Slide 7

Slide 7 text

When you need custom Observable/Operator? // Retrofit service.users() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(users -> { adapter.setItems(users); }); // RxBinding RxView.scrollChangeEvents(listView) .subscribe(event -> { int dy = event.scrollY() - event.oldScrollY(); }); When you want to model something as a stream!

Slide 8

Slide 8 text

(http://slides.com/robwormald/everything-is-a-stream#/)

Slide 9

Slide 9 text

Nearby Messaging API • Subscribe messages from other Android devices
 • Publish messages to other Android devices

Slide 10

Slide 10 text

Nearby Messaging API • Subscribe messages from other Android devices
 Modelled as Observable • Publish messages to other Android devices

Slide 11

Slide 11 text

Nearby Messaging API • Subscribe messages from other Android devices
 Modelled as Observable • Publish messages to other Android devices
 Modelled as Operator Operator

Slide 12

Slide 12 text

Nearby Messaging API • Subscribe messages from other Android devices
 Modelled as Observable • Publish messages to other Android devices
 Modelled as Operator 9JCV!1RGTCVQT!

Slide 13

Slide 13 text

Why Operator? • When and how do you want a message is published? - Publish when user clicks a button - Publish when the app loads data from local DB or a server • Why don’t you handle such trigger as an Observable?

Slide 14

Slide 14 text

(http://slides.com/robwormald/everything-is-a-stream#/)

Slide 15

Slide 15 text

Publishing a message Trigger Events (ex. button click) Message Published Events Operator

Slide 16

Slide 16 text

Publishing a message

Slide 17

Slide 17 text

Publishing a message

Slide 18

Slide 18 text

Publishing a message

Slide 19

Slide 19 text

Publishing a message

Slide 20

Slide 20 text

Publishing a message

Slide 21

Slide 21 text

What is Observable? What is Operator? • Observable is something to emit a sequence of events to the given Subscriber

Slide 22

Slide 22 text

What is Observable? What is Operator? • Observable is something to emit a sequence of events to the given Subscriber - From implementation points of view, we create an OnSubscribe instead of Observable itself Observable.create(new OnSubscribe() { … })

Slide 23

Slide 23 text

What is Observable? What is Operator? • Observable is something to emit a sequence of events to the given Subscriber - From implementation points of view, we create an OnSubscribe instead of Observable itself • Operator converts an Observable to another Observable Observable.create(new OnSubscribe() { … })

Slide 24

Slide 24 text

What is Observable? What is Operator? • Observable is something to emit a sequence of events to the given Subscriber - From implementation points of view, we create an OnSubscribe instead of Observable itself • Operator converts an Observable to another Observable - From implementation points of view, we implement a method to return a subscriber from the given subscriber Observable.create(new OnSubscribe() { … })

Slide 25

Slide 25 text

What is Observable? What is Operator? • Observable is something to emit a sequence of events to the given Subscriber - From implementation points of view, we create an OnSubscribe instead of Observable itself • Operator converts an Observable to another Observable - From implementation points of view, we implement a method to return a subscriber from the given subscriber Observable.create(new OnSubscribe() { … }) Subscriber is the core of RxJava

Slide 26

Slide 26 text

OnSubscribe Example:
 range() @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(i); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 27

Slide 27 text

How OnSubscribe works Observable.range(1, 5) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, “Completed.") );

Slide 28

Slide 28 text

How OnSubscribe works Observable.range(1, 5) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, “Completed.") );

Slide 29

Slide 29 text

How OnSubscribe works Observable.range(1, 5) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, “Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(i); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 30

Slide 30 text

How OnSubscribe works Observable.range(1, 5) .subscribe( i -> Log.i(TAG, "Output: " + 1), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, “Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(1); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 31

Slide 31 text

How OnSubscribe works Observable.range(1, 5) .subscribe( i -> Log.i(TAG, "Output: " + 2), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, “Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(2); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 32

Slide 32 text

How OnSubscribe works Observable.range(1, 5) .subscribe( i -> Log.i(TAG, "Output: " + 3), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, “Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(3); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 33

Slide 33 text

How OnSubscribe works Observable.range(1, 5) .subscribe( i -> Log.i(TAG, "Output: " + 4), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, “Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(4); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 34

Slide 34 text

How OnSubscribe works Observable.range(1, 5) .subscribe( i -> Log.i(TAG, "Output: " + 5), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, “Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(5); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 35

Slide 35 text

How OnSubscribe works Observable.range(1, 5) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, “Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(i); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 36

Slide 36 text

Operator Example: map() @Override public Subscriber super Integer> call(Subscriber super Integer> child) { return new Subscriber() { @Override public void onCompleted() { child.onCompleted(); } @Override public void onError(Throwable e) { child.onError(e); } @Override public void onNext(Integer i) { try { child.onNext(transformer.call(i)); } catch (Throwable e) { Exceptions.throwOrReport(e, this, i); } } }; }

Slide 37

Slide 37 text

Operator Example: map() @Override public Subscriber super Integer> call(Subscriber super Integer> child) { return new Subscriber() { @Override public void onCompleted() { child.onCompleted(); } @Override public void onError(Throwable e) { child.onError(e); } @Override public void onNext(Integer i) { try { child.onNext(transformer.call(i)); } catch (Throwable e) { Exceptions.throwOrReport(e, this, i); } } }; }

Slide 38

Slide 38 text

How Operator works Observable.range(1, 5) .map(i -> i * 2) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") );

Slide 39

Slide 39 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") );

Slide 40

Slide 40 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") );

Slide 41

Slide 41 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") );

Slide 42

Slide 42 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); public final Observable lift(final OperatorMap operator) { return new Observable<>(new Observable.OnSubscribe() { @Override public void call(Subscriber super Integer> o) { // new Subscriber created and being subscribed with so 'onStart' it Subscriber super Integer> st = operator.call(o); st.onStart(); onSubscribe.call(st); } }); }

Slide 43

Slide 43 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); public final Observable lift(final OperatorMap operator) { return new Observable<>(new Observable.OnSubscribe() { @Override public void call(Subscriber super Integer> o) { // new Subscriber created and being subscribed with so 'onStart' it Subscriber super Integer> st = operator.call(o); st.onStart(); onSubscribe.call(st); } }); }

Slide 44

Slide 44 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); Subscriber super Integer> st = operator.call(o); st.onStart(); onSubscribe.call(st);

Slide 45

Slide 45 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); Subscriber super Integer> st = operator.call(o); st.onStart(); onSubscribe.call(st); child subscriber

Slide 46

Slide 46 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); Subscriber super Integer> st = operator.call(o); st.onStart(); onSubscribe.call(st); child subscriber new subscriber

Slide 47

Slide 47 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); Subscriber super Integer> st = operator.call(o); st.onStart(); onSubscribe.call(st);

Slide 48

Slide 48 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); Subscriber super Integer> st = operator.call(o); st.onStart(); onSubscribe.call(st);

Slide 49

Slide 49 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(i); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 50

Slide 50 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { for (int i = start; i < start + count; i++) { if (subscriber.isUnsubscribed()) { return; } subscriber.onNext(i); } if (!subscriber.isUnsubscribed()) { subscriber.onCompleted(); } }

Slide 51

Slide 51 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { … subscriber.onNext(i); … }

Slide 52

Slide 52 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { … subscriber.onNext(i); … } @Override public void onNext(Integer i) { child.onNext(transformer.call(i)); }

Slide 53

Slide 53 text

How Operator works Observable.range(1, 5) .lift(new OperatorMap(i -> i*2)) .subscribe( i -> Log.i(TAG, "Output: " + i), throwable -> Log.e(TAG, "Error: ", throwable), () -> Log.i(TAG, "Completed.") ); @Override public void call(Subscriber super Integer> subscriber) { … subscriber.onNext(i); … } @Override public void onNext(Integer i) { child.onNext(transformer.call(i)); }

Slide 54

Slide 54 text

How Operator works • Operator defines how to create a new parent subscriber from the given child subscriber • When subscribed, it invokes its onSubscribe with that new parent subscriber • When the operators are chained, the onSubscribe invocations are also chained from child to parent • The onNext invocations are chained from parent to child

Slide 55

Slide 55 text

Nearby Messages API • Subscribe messages from other Android devices
 Modelled as Observable • Publish messages to other Android devices
 Modelled as Operator

Slide 56

Slide 56 text

Nearby Messages API • Subscribe messages from other Android devices
 Modelled as Observable • Publish messages to other Android devices
 Modelled as Operator

Slide 57

Slide 57 text

MessageSubscribeOnSubscribe Nearby.Messages .subscribe(apiClient, listener) .setResultCallback(callback); apiClient.connect();

Slide 58

Slide 58 text

MessageSubscribeOnSubscribe Nearby.Messages .subscribe(apiClient, listener) .setResultCallback(callback); apiClient.connect();

Slide 59

Slide 59 text

MessageSubscribeOnSubscribe Nearby.Messages .subscribe(apiClient, new MessageListener() { @Override public void onFound(Message message) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(message); } } }) .setResultCallback(callback); apiClient.connect();

Slide 60

Slide 60 text

MessageSubscribeOnSubscribe Nearby.Messages .subscribe(apiClient, new MessageListener() { @Override public void onFound(Message message) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(message); } } }) .setResultCallback(status -> { if (!status.isSuccess() && !subscriber.isUnsubscribed()) { subscriber.onError(new ApiStatusException(status)); } }); apiClient.connect();

Slide 61

Slide 61 text

MessageSubscribeOnSubscribe apiClient.unsubscribe(listener, callback); apiClient.disconnect();

Slide 62

Slide 62 text

subscriber.add(new Subscription() { @Override public void unsubscribe() { } @Override public boolean isUnsubscribed() { return apiClient.isConnected(); } }); MessageSubscribeOnSubscribe apiClient.unsubscribe(listener, callback); apiClient.disconnect();

Slide 63

Slide 63 text

MessageSubscribeOnSubscribe @Override public void call(final Subscriber super Message> subscriber) { final MessageListener listener = new MessageListener() { @Override public void onFound(Message message) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(message); } } }; final ApiClientWrapper.ResultHandler handler = new ApiClientWrapper.ResultHandler() { @Override public void onSuccess() { } @Override public void onError(Status status) { if (!subscriber.isUnsubscribed()) { subscriber.onError(new ApiStatusException(status)); } } }; apiClient.subscribe(listener, handler); subscriber.add(new Subscription() { @Override public void unsubscribe() { apiClient.unsubscribe(listener, handler); apiClient.disconnect(); } @Override public boolean isUnsubscribed() { return apiClient.isConnected(); } }); apiClient.connect(); }

Slide 64

Slide 64 text

Takeaways • Basically do not do anything until it is subscribed • Make sure any resources is released when unsubscribed • To make it back-pressure aware, use Subscriber#setProducer() • The thread of OnSubscribe#call() can be specified with subscribeOn() operator • There is a contract. See http://reactivex.io/ documentation/contract.html

Slide 65

Slide 65 text

Nearby Messages API • Subscribe messages from other Android devices
 Modelled as Observable • Publish messages to other Android devices
 Modelled as Operator

Slide 66

Slide 66 text

MessagePublishOperator @Override public Subscriber super Message> call(Subscriber super PublishResult> child) { final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } @Override public void onCompleted() { child.onCompleted(); } @Override public void onError(Throwable e) { child.onError(e); } }; }

Slide 67

Slide 67 text

MessagePublishOperator @Override public Subscriber super Message> call(Subscriber super PublishResult> child) { final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } @Override public void onCompleted() { child.onCompleted(); } @Override public void onError(Throwable e) { child.onError(e); } }; }

Slide 68

Slide 68 text

MessagePublishOperator final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } };

Slide 69

Slide 69 text

MessagePublishOperator final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } };

Slide 70

Slide 70 text

MessagePublishOperator final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } };

Slide 71

Slide 71 text

MessagePublishOperator final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } };

Slide 72

Slide 72 text

MessagePublishOperator final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } };

Slide 73

Slide 73 text

MessagePublishOperator final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } };

Slide 74

Slide 74 text

MessagePublishOperator @Override public Subscriber super Message> call(Subscriber super PublishResult> child) { final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } @Override public void onCompleted() { child.onCompleted(); } @Override public void onError(Throwable e) { child.onError(e); } }; }

Slide 75

Slide 75 text

MessagePublishOperator @Override public Subscriber super Message> call(Subscriber super PublishResult> child) { final ApiClientWrapper apiClient = new ApiClientWrapper(this.context); return new Subscriber(child) { @Override public void onNext(Message message) { Nearby.Messages.publish(apiClient, message).setResultCallback(status -> { if (status.isSuccess()) { child.onNext(new PublishResult(message)); } else { child.onError(new ApiStatusException(status)); } }); } @Override public void onCompleted() { child.onCompleted(); } @Override public void onError(Throwable e) { child.onError(e); } }; }

Slide 76

Slide 76 text

MessagePublishOperator @Override public void onCompleted() { child.onCompleted(); } @Override public void onError(Throwable e) { child.onError(e); }

Slide 77

Slide 77 text

MessagePublishOperator private void init() { add(new Subscription() { @Override public void unsubscribe() { apiClient.unpublish(); apiClient.disconnect(); } @Override public boolean isUnsubscribed() { return apiClient.isConnected(); } }); }

Slide 78

Slide 78 text

MessagePublishOperator private void init() { add(new Subscription() { @Override public void unsubscribe() { apiClient.unpublish(); apiClient.disconnect(); } @Override public boolean isUnsubscribed() { return apiClient.isConnected(); } }); }

Slide 79

Slide 79 text

Takeaways • Call the child subscriber’s onNext()/onError()/onCompleted appropriately • Make sure any resources and the newly created parent subscriber are released when the child subscriber is unsubscribed • You can use new Subscriber(Subscriber) to make sure the subscription and the back-pressure chains ✓ Note, in that case, all the subscriptions are shared with a child subscriber, which may cause an unexpected behaviour in some cases (See http://akarnokd.blogspot.jp/2015/05/pitfalls- of-operator-implementations.html) • Take care of multiple subscriptions

Slide 80

Slide 80 text

Summary • Subscriber is the core of RxJava • Custom Observable is pretty easy to create • Custom Operator is a bit more hard - Take care of subscriptions and back pressure • Read “RxJava Advanced” - Pitfalls of operator implementations (part 1)
 http://akarnokd.blogspot.jp/2015/05/pitfalls-of-operator- implementations.html

Slide 81

Slide 81 text

Thank you! Hiroshi Kurokawa (ࠇ઒ ༸) @hydrakecat https://github.com/hkurokawa https://speakerdeck.com/hkurokawa hydrakecat.hatenablog.jp