Share
Embed
Transcript
79 slides
-
Lambda Notation new Func1<String, Integer>() { @Override public Integer call(String
s) { return s.length(); } }; s -> s.length(); -
Lambda Notation new Func1<String, Integer>() { @Override public Integer call(String
s) { return s.length(); } }; s -> { return s.length(); } s -> s.length() -
Lambda Notation new Func1<String, Integer>() { @Override public Integer call(String
s) { return s.length(); } }; (String s) -> { return s.length(); } s -> s.length() …is the same as… -
Lambda Notation new Func1<String, Integer>() { @Override public Integer call(String
s) { return s.length(); } }; (String s) -> { return s.length(); } s -> s.length() …is the same as… …is the same as… -
Observable<String> obs = Observable.just("Hello!"); obs.map(s -> s.length()); obs.subscribe(i -> System.out.println(i));
Observable.just("Hello!") .map(s -> s.length()) .subscribe(i -> System.out.println(i)); Versus DOES NOT WORK -
Observable<String> step1 = Observable.just("Hello!"); Observable<Integer> step2 = step1.map(s ->
s.length()); step2.subscribe(i -> System.out.println(i)); -
Observable<String> step1 = Observable.just("Hello!"); Observable<Integer> step2 = step1.map(s ->
s.length()); step2.subscribe(i -> System.out.println(i)); -
Observable<String> step1 = Observable.just("Hello!"); Observable<Integer> step2 = step1.map(s ->
s.length()); step2.subscribe(i -> System.out.println(i)); -
Creating Observables 101 • just(object) • from(iterable) • Whatever Retrofit
is doing • create() NO -
Difficulties • Observable contract • onNext* -> onCompleted | onError
• Unsubscription • Backpressure -
class MyClass { String value; Observable<String> value() { return
Observable.create(subscriber -> { subscriber.onNext(value); subscriber.onCompleted(); }); } } ??? -
class MyClass { String value; Observable<String> value() { return
Observable.create(subscriber -> { subscriber.onNext(value); subscriber.onCompleted(); }); } } BAD -
class MyClass { String value; Observable<String> value() { return
Observable.create(subscriber -> { subscriber.onNext(value); subscriber.onCompleted(); }); } } return Observable.fromCallable(() -> value); -
class MyClass { String value; Observable<String> value() { return
Observable.create(subscriber -> { subscriber.onNext(value); subscriber.onCompleted(); }); } } return Observable.defer(() -> Observable.just(value)); -
class MyClass { BehaviorSubject<String> value; Observable<String> value() { return
value.asObservable(); } void setValue(String s) { value.onNext(s); } } -
Lessons • Learn your creation methods! • Resort to create()
when appropriate • (which is almost never) -
Types • map: T -> R • flatMap: T ->
Observable<R> -
Exercise • Find an API with search • Query search
results via Observable • Request extra data on results via Observable • Use only one subscribe() -
flatMap vs. concatMap • Same, except… • #ordermatters • flatMap
-> Unordered merge • concatMap -> Ordered merge -
toList() • Item(s) -> List<Item> • “Why does nothing happen?!”
• Missing onCompleted -
List of Lists Observable<List<String>> obs = /* ...etc... */;
obs.flatMap(list -> Observable.from(list)) .map(/* ...something... */) .toList(); obs.flatMap( list -> Observable.from(list) .map(/* ...something... */) .toList() ); -
List of Lists Observable<List<String>> obs = /* ...etc... */;
obs.flatMap(list -> Observable.from(list)) .map(/* ...something... */) .toList(); obs.flatMap( list -> Observable.from(list) .map(/* ...something... */) .toList() ); result: 1 list -
List of Lists Observable<List<String>> obs = /* ...etc... */;
obs.flatMap(list -> Observable.from(list)) .map(/* ...something... */) .toList(); obs.flatMap( list -> Observable.from(list) .map(/* ...something... */) .toList() ); result: 1 list result: N lists -
take(1) vs. first() • take(1) - emits once, or not
at all Observable.empty().take(1) • first() - emits once, or crashes Observable.empty().first() crash! emits nothing -
cache() vs replay() • Don’t use cache() • Use replay()
• cache() == replay().autoConnect() -
Operators I Have Written • OperatorNullToDefault • OperatorSortLists • OperatorSubscribeUntil
• ContinueWithIfEmptyOperator -
Why not? • Each operator —> different flow • Framework
handles common flows • Resort to custom operators for unique flows • Operators are hard! -
Lessons • Know your scheduler-based operators • Use subscribeOn once
• Use observeOn liberally -
Observer<Integer> observer = /* some Observer */ o1.subscribe(observer); o2.subscribe(observer); Subscriber<Integer>
subscriber = /* some Subscriber */ o1.subscribe(subscriber); o2.subscribe(subscriber); Versus -
• Observer is an interface • Subscriber is a class
• Observer is stateless • Subscriber is stateful -
Observer<Integer> observer = /* some Observer */ o1.subscribe(observer); o2.subscribe(observer); Subscriber<Integer>
subscriber = /* some Subscriber */ o1.subscribe(subscriber); o2.subscribe(subscriber); Versus Can blow up in your face -
Memory Leaks • Subscriptions can leak! • Detect w/ LeakCanary
• Solution: Unsubscribe -
onError Uses • Unexpected exceptions • Unrecoverable streams • Flow
control Blame Retrofit 1 -
Debugging onError Exception in thread "RxCachedThreadScheduler-1" java.lang.IllegalStateException: Exception thrown on
Scheduler.Worker thread. Add `onError` handling. at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:60) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: rx.exceptions.OnErrorNotImplementedException: Sequence contains no elements at rx.Observable$26.onError(Observable.java:7881) at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:159) at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:120) at rx.internal.operators.OperatorSubscribeOn$1$1$1.onError(OperatorSubscribeOn.java:71) at rx.internal.operators.OperatorSingle$ParentSubscriber.onCompleted(OperatorSingle.java:131) at rx.internal.operators.OperatorTake$1.onCompleted(OperatorTake.java:53) at rx.Observable$EmptyHolder$1.call(Observable.java:1073) at rx.Observable$EmptyHolder$1.call(Observable.java:1070) at rx.Observable$2.call(Observable.java:162) at rx.Observable$2.call(Observable.java:154) at rx.Observable$2.call(Observable.java:162) at rx.Observable$2.call(Observable.java:154) at rx.Observable.unsafeSubscribe(Observable.java:8098) at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) ... 7 more Caused by: java.util.NoSuchElementException: Sequence contains no elements ... 18 more No references to my code! -
Implement onError Observable.empty() .first() .subscribeOn(Schedulers.io()) .subscribe(o -> System.out.println(o), err ->
{ throw new OnErrorNotImplementedException("Source!", err); }); Caused by: rx.exceptions.OnErrorNotImplementedException: Source! at net.danlew.experiments.Tester.lambda$main$1(Tester.java:52) -
Implement onError Observable.empty() .first() .subscribeOn(Schedulers.io()) .subscribe(o -> System.out.println(o), err ->
{ throw new OnErrorNotImplementedException("Source!", err); }); Caused by: rx.exceptions.OnErrorNotImplementedException: Source! at net.danlew.experiments.Tester.lambda$main$1(Tester.java:52) -
Implement onError Observable.empty() .first() .subscribeOn(Schedulers.io()) .subscribe(o -> System.out.println(o), err ->
{ throw new OnErrorNotImplementedException("Source!", err); }); Caused by: rx.exceptions.OnErrorNotImplementedException: Source! at net.danlew.experiments.Tester.lambda$main$1(Tester.java:52) -
Sharing • Wanted: Multiple subscribers retrofitRequest.subscribe(/* do a thing */);
retrofitRequest.subscribe(/* do another thing */); • Bad: Duplicate expensive operations -
Sharing ConnectableObservable<Long> obs = Observable.fromCallable(() -> System.nanoTime()) .publish(); obs.subscribe(l
-> System.out.println("1: " + l)); obs.subscribe(l -> System.out.println("2: " + l)); obs.connect(); -
Sharing ConnectableObservable<Long> obs = Observable.fromCallable(() -> System.nanoTime()) .publish(); obs.subscribe(l
-> System.out.println("1: " + l)); obs.subscribe(l -> System.out.println("2: " + l)); obs.connect(); -
Sharing ConnectableObservable<Long> obs = Observable.fromCallable(() -> System.nanoTime()) .publish(); obs.subscribe(l
-> System.out.println("1: " + l)); obs.subscribe(l -> System.out.println("2: " + l)); obs.connect(); -
Sharing ConnectableObservable<Long> obs = Observable.fromCallable(() -> System.nanoTime()) .publish(); obs.subscribe(l
-> System.out.println("1: " + l)); obs.subscribe(l -> System.out.println("2: " + l)); obs.connect(); -
Sharing ConnectableObservable<Long> obs = Observable.fromCallable(() -> System.nanoTime()) .publish(); obs.subscribe(l
-> System.out.println("1: " + l)); obs.subscribe(l -> System.out.println("2: " + l)); obs.connect(); -
refCount Observable<Long> obs = Observable.fromCallable(() -> System.nanoTime()) .publish() .refCount();
obs.subscribe(l -> System.out.println("1: " + l)); obs.subscribe(l -> System.out.println("2: " + l)); // Output 1: 395093905388878 2: 395093906717841 -
refCount Observable<Long> obs = Observable.fromCallable(() -> System.nanoTime()) .publish() .refCount();
obs.subscribe(l -> System.out.println("1: " + l)); obs.subscribe(l -> System.out.println("2: " + l)); // Output 1: 395093905388878 2: 395093906717841 -
refCount Observable<Long> obs = Observable.fromCallable(() -> System.nanoTime()) .publish() .refCount();
obs.subscribe(l -> System.out.println("1: " + l)); obs.subscribe(l -> System.out.println("2: " + l)); // Output 1: 395093905388878 2: 395093906717841 huh? -
Lessons • Share cold observables • Explicitly call connect() •
Use autoConnect()