Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A Practical Guide To understand Rx Streams

A Practical Guide To understand Rx Streams

RxJava has helped us solve complex problems with ease and made our code manageable. In this talk we discuss how Capital One Wallet app uses RxJava 2, with practical examples. In our app, we follow Uncle Bob’s Clean Architecture with reactive approach. Our app has come a long way from using callbacks to RxJava 1 and eventually to RxJava 2. During this talk, we will discuss lots of tips and tricks that can help you prevent common mistakes.

This talk is suitable for intermediate users of Rx.

Mayank Mehta

August 26, 2017
Tweet

More Decks by Mayank Mehta

Other Decks in Technology

Transcript

  1. Agenda • Terminology • Operators • Subject • Schedulers •

    Reactive Source • Architecture • Appendix
  2. Architecture View state Repository Use case Presenter View Presentation Layer

    Domain Layer Data Layer Observable<T> Observable<T> Observable<T>
  3. MVVM + RxJava • Modular • Testable • Concurrency is

    easy • Better code • Happy developers
  4. Observable subscribe with observer public interface Observer<T> { void onSubscribe(Disposable

    d); void onNext(T t); void onError(Throwable e); void onComplete() }
  5. Observable subscribe with observer public interface Observer<T> { void onSubscribe(Disposable

    d); void onNext(T t); void onError(Throwable e); void onComplete(); }
  6. Observable subscribe with observer public interface Observer<T> { void onSubscribe(Disposable

    d); void onNext(T t); void onError(Throwable e); void onComplete(); }
  7. Observable subscribe with observer public interface Observer<T> { void onSubscribe(Disposable

    d); void onNext(T t); void onError(Throwable e); void onComplete(); }
  8. Observable subscribe with observer public interface Observer<T> { void onSubscribe(Disposable

    d); void onNext(T t); void onError(Throwable e); void onComplete(); }
  9. What are Operators? • They are functions to create, transform,

    filter, combine etc reactive source • Like builder pattern, you can chain methods, except order matters
  10. Concat • Concatenates output of multiple observables • Subscribes to

    one observable at a time and waits for current one to complete 1 Concat 4 3 5 1 2 3 4 5 2
  11. FlatMap • Transform the items emitted by an Observable into

    Observables • Flatten the emissions • Emissions may interleave 1 FlatMap 3 2 4 1 2 3 4
  12. Zip • Combine emissions of multiple Observables • Waits for

    one item from each source Zip 1 1A A 2 B 2B
  13. Debounce • Only emit an item from an Observable if

    a particular timespan has passed without it emitting another item Debounce (500 msec) 1 3 2 4 5 1 4 5
  14. What are Subject? • Broadcasts to many • Broadcasts when

    no one is listening • Listens from many pc - http://www.iconsplace.com/e13491-icons/walkie-talkie-radio-icon
  15. Subject public abstract class Subject<T> extends Observable<T> implements Observer<T> {

    } public interface Observer<T> { void onSubscribe(Disposable d); void onNext(T t); void onError(Throwable e); void onComplete(); }
  16. Do you really need Subject? public abstract class Subject<T> extends

    Observable<T> implements Observer<T> { } public interface Observer<T> { void onSubscribe(Disposable d); void onNext(T t); void onError(Throwable e); void onComplete(); } Terminal event
  17. RxRelay to Rescue public abstract class Relay<T> extends Observable<T> implements

    Consumer<T> { } public interface Consumer<T> { void accept(@NonNull T t) throws Exception; }
  18. RxRelay to Rescue public abstract class Relay<T> extends Observable<T> implements

    Consumer<T> { } public interface Consumer<T> { void accept(@NonNull T t) throws Exception; }
  19. PublishRelay • Only emit items after observer subscribes • Login/Logout

    events PublishRelay 0 2 1 3 subscribe() 1 2 2 3 3 subscribe()
  20. BehaviorRelay • Emits most recent item and all subsequent items

    BehaviorRelay 0 2 1 3 1 2 2 3 3 0 1 subscribe() subscribe()
  21. Legacy code with callback public interface GetLocationCallback { void onSuccess(@NonNull

    Location location); void onFailure(@Nullable Status locationSettingsStatus); } public class LocationService { public void getCurrentLocation(final GetLocationCallback newPendingLocationRequest) { } }
  22. Source wraps callback with create public Single<Location> getCurrentLocation() { return

    Single.create(emitter -> { final GetLocationCallback callback = new GetLocationCallback() { @Override public void onSuccess(@NonNull Location location) { emitter.onSuccess(location); } @Override public void onFailure(@Nullable Status locationSettingsStatus) { emitter.onError(new LocationServiceException(locationSettingsStatus)); } }; getCurrentLocation(callback); }); }
  23. Source wraps callback with create public Single<Location> getCurrentLocation() { return

    Single.create(emitter -> { final GetLocationCallback callback = new GetLocationCallback() { @Override public void onSuccess(@NonNull Location location) { emitter.onSuccess(location); } @Override public void onFailure(@Nullable Status locationSettingsStatus) { emitter.onError(new LocationServiceException(locationSettingsStatus)); } }; getCurrentLocation(callback); }); }
  24. Source wraps callback with create public Single<Location> getCurrentLocation() { return

    Single.create(emitter -> { final GetLocationCallback callback = new GetLocationCallback() { @Override public void onSuccess(@NonNull Location location) { emitter.onSuccess(location); } @Override public void onFailure(@Nullable Status locationSettingsStatus) { emitter.onError(new LocationServiceException(locationSettingsStatus)); } }; getCurrentLocation(callback); }); }
  25. Source wraps callback with create public Single<Location> getCurrentLocation() { return

    Single.create(emitter -> { final GetLocationCallback callback = new GetLocationCallback() { @Override public void onSuccess(@NonNull Location location) { emitter.onSuccess(location); } @Override public void onFailure(@Nullable Status locationSettingsStatus) { emitter.onError(new LocationServiceException(locationSettingsStatus)); } }; getCurrentLocation(callback); }); }
  26. Source wraps function with fromCallable Single.fromCallable(() -> someHeavyComputation()); • Defers

    execution until subscription • Handles checked exception • Invokes function for each observer that subscribes
  27. Types of Schedulers Method Description # of Thread computation Event

    loops, callbacks and computation work Available processors io Asynchronously blocking IO Unbound newThread Casual use Unbound
  28. Defaults to current thread Observable.fromCallable(() -> getName()) // On Main

    Thread .doOnNext(id -> {}) // On Main Thread .subscribe() // On Main Thread
  29. subscribeOn + observeOn Observable.fromCallable(() -> getName()) // Computation Thread .subscribeOn(Schedulers.computation())

    .observeOn(AndroidSchedulers.mainThread()) .doOnNext(id -> {}) // Main Thread .subscribe()
  30. subscribeOn + observeOn Observable.fromCallable(() -> getName()) // Computation Thread .subscribeOn(Schedulers.computation())

    .observeOn(AndroidSchedulers.mainThread()) .doOnNext(id -> {}) // Main Thread .subscribe() // Main Thread
  31. subscribeOn + observeOn + observeOn Observable.fromCallable(() -> getName()) // Computation

    Thread .subscribeOn(Schedulers.computation()) .observeOn(Schedulers.io()) .doOnNext(id -> {}) .observeOn(AndroidSchedulers.mainThread()) .subscribe()
  32. subscribeOn + observeOn + observeOn Observable.fromCallable(() -> getName()) // Computation

    Thread .subscribeOn(Schedulers.computation()) .observeOn(Schedulers.io()) .doOnNext(id -> {}) // IO Thread .observeOn(AndroidSchedulers.mainThread()) .subscribe()
  33. subscribeOn + observeOn + observeOn Observable.fromCallable(() -> getName()) // Computation

    Thread .subscribeOn(Schedulers.computation()) .observeOn(Schedulers.io()) .doOnNext(id -> {}) // IO Thread .observeOn(AndroidSchedulers.mainThread()) .subscribe() // Main Thread
  34. Architecture View state Repository Use case Presenter View Presentation Layer

    Domain Layer Data Layer Observable<T> Observable<T> Observable<T>
  35. Cloud Store discountCloudStore .fetchNearbyDiscount(NearbyRequestBody(lat, lng))// Single<NearbyResponse> .filter { it.nearbyMerchants.isNotEmpty() }

    // Maybe<NearbyResponse> .map { it.nearbyMerchants } .doOnSuccess { discountCacheStore.updateNearbyCache(it) }
  36. Cloud Store discountCloudStore .fetchNearbyDiscount(NearbyRequestBody(lat, lng))// Single<NearbyResponse> .filter { it.nearbyMerchants.isNotEmpty() }

    // Maybe<NearbyResponse> .map { it.nearbyMerchants } // Maybe<List<Merchants>> .doOnSuccess { discountCacheStore.updateNearbyCache(it) }
  37. Cloud Store discountCloudStore .fetchNearbyDiscount(NearbyRequestBody(lat, lng))// Single<NearbyResponse> .filter { it.nearbyMerchants.isNotEmpty() }

    // Maybe<NearbyResponse> .map { it.nearbyMerchants } // Maybe<List<Merchants>> .doOnSuccess { discountCacheStore.updateNearbyCache(it) } // Side effect
  38. Architecture – Domain Layer View state Repository Use case Presenter

    View public interface WalletUseCase<I, O> { O execute(I input); }
  39. Declaring view state sealed class DiscountsViewState { object Loading :

    DiscountsViewState() data class Error(val error: Throwable) : DiscountsViewState() data class DataReady(val merchantWrappers: List<MerchantWrapper>) :DiscountsViewState() }
  40. Declaring view state sealed class DiscountsViewState { object Loading :

    DiscountsViewState() data class Error(val error: Throwable) : DiscountsViewState() data class DataReady(val merchantWrappers: List<MerchantWrapper>) :DiscountsViewState() }
  41. Declaring view state sealed class DiscountsViewState { object Loading :

    DiscountsViewState() data class Error(val error: Throwable) : DiscountsViewState() data class DataReady(val merchantWrappers: List<MerchantWrapper>) :DiscountsViewState() }
  42. onDestroy onDestroy onDetach onDetach onAttach onAttach onCreate onCreate Presenter View

    Exposes PublishRelay as observable for input Subscribes to BehaviorRelay and updates view Dispose composite disposable for UI Dispose composite disposable for Presenter
  43. Presenter View Presentation Layer onAttach Presenter View onDetach Data is

    received Requests Data Creates UI View State Passes to BehaviorRelay
  44. Presenter View Presentation Layer onAttach Presenter View onDetach Data is

    received Requests Data Presenter View onAttach Subscribes to BehaviorRelay Updates UI with missed view state Only distinct view state Creates UI View State Passes to BehaviorRelay
  45. Presenter – onCreate compositePresenterDisposable += fetchNearbyCoupons // PublishRelay - to

    initiate stream .flatMap { fetchNearbyDiscountsUseCase .execute() // Maybe<List<MerchantWrapper>> } // DiscountViewState
  46. Presenter – onCreate compositePresenterDisposable += fetchNearbyCoupons // PublishRelay - to

    initiate stream .flatMap { fetchNearbyDiscountsUseCase .execute() // Maybe<List<MerchantWrapper>> .toObservable() // Maybe to Observable } // DiscountViewState
  47. Presenter – onCreate compositePresenterDisposable += fetchNearbyCoupons // PublishRelay - to

    initiate stream .flatMap { fetchNearbyDiscountsUseCase .execute() // Maybe<List<MerchantWrapper>> .toObservable() // Maybe to Observable .map { DiscountsViewState.DataReady(it) } // Transform } // DiscountViewState
  48. Presenter – onCreate compositePresenterDisposable += fetchNearbyCoupons // PublishRelay - to

    initiate stream .flatMap { fetchNearbyDiscountsUseCase .execute() // Maybe<List<MerchantWrapper>> .toObservable() // Maybe to Observable .map { DiscountsViewState.DataReady(it) } // Transform .onErrorReturn { t: Throwable -> DiscountsViewState.Error(t) } // handle error } // DiscountViewState
  49. Presenter – onCreate compositePresenterDisposable += fetchNearbyCoupons // PublishRelay - to

    initiate stream .flatMap { fetchNearbyDiscountsUseCase .execute() // Maybe<List<MerchantWrapper>> .toObservable() // Maybe to Observable .map { DiscountsViewState.DataReady(it) } // Transform .onErrorReturn { t: Throwable -> DiscountsViewState.Error(t) } // handle error .startWith(DiscountsViewState.Loading) } // DiscountViewState
  50. Presenter – onCreate compositePresenterDisposable += fetchNearbyCoupons // PublishRelay - to

    initiate stream .flatMap { fetchNearbyDiscountsUseCase .execute() // Maybe<List<MerchantWrapper>> .toObservable() // Maybe to Observable .map { DiscountsViewState.DataReady(it) } // Transform .onErrorReturn { t: Throwable -> DiscountsViewState.Error(t) } // handle error .startWith(DiscountsViewState.Loading) } // DiscountViewState .observeOn(AndroidSchedulers.mainThread()) // RxAndroid – Main Thread
  51. Presenter – onCreate compositePresenterDisposable += fetchNearbyCoupons // PublishRelay - to

    initiate stream .flatMap { fetchNearbyDiscountsUseCase .execute() // Maybe<List<MerchantWrapper>> .toObservable() // Maybe to Observable .map { DiscountsViewState.DataReady(it) } // Transform .onErrorReturn { t: Throwable -> DiscountsViewState.Error(t) } // handle error .startWith(DiscountsViewState.Loading) } // DiscountViewState .observeOn(AndroidSchedulers.mainThread()) // RxAndroid – Main Thread .subscribe({ viewState -> statesBehaviorRelay.accept(viewState) }, { // processError })
  52. Presenter – onCreate compositePresenterDisposable += fetchNearbyCoupons // PublishRelay - to

    initiate stream .flatMap { fetchNearbyDiscountsUseCase .execute() // Maybe<List<MerchantWrapper>> .toObservable() // Maybe to Observable .map { DiscountsViewState.DataReady(it) } // Transform .onErrorReturn { t: Throwable -> DiscountsViewState.Error(t) } // handle error .startWith(DiscountsViewState.Loading) } // DiscountViewState .observeOn(AndroidSchedulers.mainThread()) // RxAndroid – Main Thread .subscribe({ viewState -> statesBehaviorRelay.accept(viewState) }, { // processError })
  53. Composite Disposable – clear Vs dispose • Clear will dispose

    all added disposables • Dispose is like clear except you can not add any more disposables
  54. Blogs to bookmark • Jake Wharton - http://jakewharton.com/blog/ • Dan

    Lew - http://blog.danlew.net/ • Kaushik Gopal - https://kaush.co/ • David Karnok - http://akarnokd.blogspot.com/
  55. What are Subjects • Inherit Observable and implements Observer •

    Hot Observables • toSerialized() to make Observer method thread safe • Type of Subjects • PublishSubject • BehaviorSubject • ReplaySubject • AsyncSubject
  56. Architecture – Presentation Layer • Presenter is aware when view

    is attached and detached • Presenter creates view states and passes it to BehaviorRelay • Whenever view is attached it gets its states from BehaviorRelay