Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Practical RxJava for Android

1,155 views

Published on

A practical guide to using RxJava on Android. Tips for improving your app architecture with reactive programming. What are the advantages and disadvantages of using RxJava over standard architecture? And how to connect with other popular Android libraries?
Presented at GDG DevFest The Netherlands 2016.

Published in: Technology
  • Be the first to comment

Practical RxJava for Android

  1. 1. Practical RxJava for Android Tomáš Kypta @TomasKypta
  2. 2. What’s RxJava?
  3. 3. What’s RxJava? • composable data flow • push concept • combination of • observer pattern • iterator pattern • functional programming
  4. 4. RxJava…not sure how Android apps…
  5. 5. Typical non-reactive app Event Source Views Network DB … Listener Listener Listener Listener logic logiclogiclogic State
  6. 6. Reactive app Transformation Event Source Observable Observable Observable Observable … ObserverObserver Views Network DB
  7. 7. RxJava data flow Observable .from(new String[]{"Hello", "Droidcon!"}) creation
  8. 8. RxJava data flow Observable .from(new String[]{"Hello", "Droidcon!"}) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(Locale.getDefault()); } }) creation
  9. 9. RxJava data flow Observable .from(new String[]{"Hello", "Droidcon!"}) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(Locale.getDefault()); } }) .reduce(new Func2<String, String, String>() { @Override public String call(String s, String s2) { return s + ' ' + s2; } }) creation transformation
  10. 10. RxJava data flow Observable .from(new String[]{"Hello", "Droidcon!"}) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(Locale.getDefault()); } }) .reduce(new Func2<String, String, String>() { @Override public String call(String s, String s2) { return s + ' ' + s2; } }) .subscribe(new Action1<String>() { @Override public void call(String s) { Timber.i(s); } }); creation transformation subscription
  11. 11. Java 8 & Android • use Retrolambda • or jack compiler • usable since build plugin 2.2.0
  12. 12. RxJava data flow Observable .from(new String[]{"Hello", "Droidcon!"}) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(Locale.getDefault()); } }) .reduce(new Func2<String, String, String>() { @Override public String call(String s, String s2) { return s + ' ' + s2; } }) .subscribe(new Action1<String>() { @Override public void call(String s) { Timber.i(s); } }); creation transformation subscription
  13. 13. RxJava data flow with Java 8 creation transformation subscription Observable .from(new String[]{"Hello", "Droidcon!"}) .map(s -> s.toUpperCase(Locale.getDefault())) .reduce((s,s2) -> s + ' ' + s2) .subscribe(s -> Timber.i(s));
  14. 14. Key parts • Observable • Observer or Subscriber • onNext(T) • onCompleted() • onError(Throwable) • Subject
  15. 15. What is RxJava good for? • making code simple and readable • …with Java 8 • Async processing • no AsyncTask, AsyncTaskLoader, …
  16. 16. Why…??
  17. 17. Async composition • Async composition • RxJava offers simple chaining of async operations • eliminates callback hell
  18. 18. RxJava 1 vs. RxJava 2
  19. 19. RxJava 1 vs. RxJava 2 • RxJava 2 in RC now • limited support in libraries • they will coexist for some time • different group ids • io.reactivex vs. io.reactivex.rxjava2 • different package names • rx vs. io.reactivex • https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0
  20. 20. Operators
  21. 21. Operators • work on Observable • return an Observable • can be chained
  22. 22. Common mistakes • assuming mutability • operators return new Observable Observable<String> observable = Observable.from(new String[]{"Hello", "Droidcon!"}); obserable.map(s -> s.toUpperCase(Locale.getDefault())); obserable.reduce((s,s2) -> s + ' ' + s2);
  23. 23. Common Mistakes •assuming mutability •operators return new Observable Observable<String> observable = Observable.from(new String[]{"Hello", "Droidcon!"}) .map(s -> s.toUpperCase(Locale.getDefault())) .reduce((s,s2) -> s + ' ' + s2);
  24. 24. Marble diagrams • RxMarbles
  25. 25. Subscription
  26. 26. Subscription Subscription s = mAppInfoProvider.getAppsObservable() .subscribe( appInfo -> Timber.i(appInfo.getPackageName() );
  27. 27. Subscription Subscription s = mAppInfoProvider.getAppsObservable() .subscribe( appInfo -> Timber.i(appInfo.getPackageName() ); s.unsubscribe();
  28. 28. Subscription Subscription s = mAppInfoProvider.getAppsObservable() .subscribe( appInfo -> Timber.i(appInfo.getPackageName() ); s.unsubscribe(); Timber.i("unsubscribed: " + s.isUnsubscribed());
  29. 29. Subscription Subscription s = mAppInfoProvider.getAppsObservable() .subscribe( appInfo -> Timber.i(appInfo.getPackageName() ); s.unsubscribe(); Timber.i("unsubscribed: " + s.isUnsubscribed());
  30. 30. CompositeSubscription CompositeSubscription compositeSubscription = new CompositeSubscription();
  31. 31. CompositeSubscription CompositeSubscription compositeSubscription = new CompositeSubscription(); compositeSubscription.add(subscription);
  32. 32. CompositeSubscription CompositeSubscription compositeSubscription = new CompositeSubscription(); compositeSubscription.add(subscription); compositeSubscription.unsubscribe();
  33. 33. CompositeSubscription CompositeSubscription compositeSubscription = new CompositeSubscription(); compositeSubscription.add(subscription); compositeSubscription.unsubscribe(); compositeSubscription.clear();
  34. 34. CompositeSubscription CompositeSubscription compositeSubscription = new CompositeSubscription(); compositeSubscription.add(subscription); compositeSubscription.unsubscribe(); compositeSubscription.clear();
  35. 35. Bridging non-Rx APIs
  36. 36. Observable creation • create() • better to avoid private Object getData() {...} public Observable<Object> getObservable() { return Observable.create(subscriber -> { subscriber.onNext(getData()); subscriber.onCompleted(); }); }
  37. 37. Observable creation • just() private Object getData() {...} public Observable<Object> getObservable() { return Observable.just(getData()); } !
  38. 38. just() & defer() • defer() private Object getData() {...} public Observable<Object> getObservable() { return Observable.defer( () -> Observable.just(getData()) ); }
  39. 39. Observable creation • fromCallable() • callable invoked when an observer subscribes private Object getData() {…} public Observable<Object> getObservable() { return Observable.fromCallable(new Callable<Object>() { @Override public String call() throws Exception { return getData(); } }); }
  40. 40. Observable creation from async APIs • fromEmitter() Observable.<Event>fromEmitter(emitter -> { Callback listener = new Callback() { @Override public void onSuccess(Event e) { emitter.onNext(e); if (e.isLast()) emitter.onCompleted(); } @Override public void onFailure(Exception e) { emitter.onError(e); } }; AutoCloseable c = api.someAsyncMethod(listener); emitter.setCancellation(c::close); }, BackpressureMode.BUFFER);
  41. 41. Subject
  42. 42. Subject • Observable & Observer • bridge between non-Rx API
  43. 43. Subject // consume anObservable.subscribe(aSubject); // produce aSubject.subscribe(aSubscriber);
  44. 44. Subject // bridging & multicasting aSubject.subscribe(subscriber1); aSubject.subscribe(subscriber2); aSubject.onNext(item1); aSubject.onNext(item2); aSubject.onNext(item2); aSubject.onCompleted()
  45. 45. Subject • stateful • terminal state • don’t pass data after onComplete() or onError()
  46. 46. Subject • AsyncSubject • BehaviorSubject • ReplaySubject • PublishSubject • SerializedSubject
  47. 47. RxRelay
  48. 48. RxRelay • safer cousin of Subject • https://github.com/JakeWharton/RxRelay
  49. 49. RxRelay • Relay = Subject - onComplete() - onError() • Relay = Observable & Action1 • call() instead of onNext()
  50. 50. Subject vs. RxRelay • Subject • stateful • Relay • stateless
  51. 51. RxRelay Relay relay = … relay.subscribe(observer); relay.call(A); relay.call(B);
  52. 52. Threading
  53. 53. Threading • Parts of a data flow can run on different threads! • Threading in RxJava defined by Schedulers
  54. 54. Schedulers • computation() • io() • newThread() • from(Executor)
  55. 55. Android Schedulers • mainThread() • from(Looper)
  56. 56. Threading • operators have their default Schedulers • check Javadoc!
  57. 57. Threading • just() • current thread • delay(long, TimeUnit) • computation() thread
  58. 58. Threading • subscribeOn(Scheduler) • subscribes to Observable on the Scheduler • observeOn(Scheduler) • Observable emits on the Scheduler
  59. 59. subscribeOn() • multiple calls useless • only the first call works! • for all operators
  60. 60. observeOn() • can be called multiple times • changes Scheduler downstream
  61. 61. Side effect methods
  62. 62. Side effect methods • doOnNext() • doOnError() • doOnCompleted() • doOnSubscribe() • doOnUnsubscribe() • …
  63. 63. Async composition
  64. 64. Async composition apiEndpoint.login() .doOnNext(accessToken -> storeCredentials(accessToken))
  65. 65. Async composition apiEndpoint.login() .doOnNext(accessToken -> storeCredentials(accessToken)) .flatMap(accessToken -> serviceEndpoint.getUser())
  66. 66. Async composition apiEndpoint.login() .doOnNext(accessToken -> storeCredentials(accessToken)) .flatMap(accessToken -> serviceEndpoint.getUser()) .flatMap(user -> serviceEndpoint.getUserContact(user.getId()))
  67. 67. Async composition apiEndpoint.login() .doOnNext(accessToken -> storeCredentials(accessToken)) .flatMap(accessToken -> serviceEndpoint.getUser()) .flatMap(user -> serviceEndpoint.getUserContact(user.getId()))
  68. 68. Async composition
  69. 69. Android & RxJava
  70. 70. Android Lifecycle • few complications • continuing subscription during configuration change • memory leaks
  71. 71. Android Lifecycle • continuing subscription during configuration change • replay() • don’t use cache()
  72. 72. Android Lifecycle • memory leaks • bind to Activity/Fragment lifecycle • use RxLifecycle
  73. 73. RxLifecycle
  74. 74. RxLifecycle • auto unsubscribe based on Activity/Fragment lifecycle myObservable .compose( RxLifecycle.bindUntilEvent( lifecycleObservable, ActivityEvent.DESTROY ) ) .subscribe(…); myObservable .compose(RxLifecycle.bindActivity(lifecycleObs)) .subscribe(…);
  75. 75. RxLifecycle • How to obtain ActivityEvent or FragmentEvent? A. rxlifecycle-components + subclass RxActivity, RxFragment B. Navi + rxlifecycle-navi C. Write it yourself
  76. 76. RxLifecycle public class MyActivity extends RxActivity { @Override public void onResume() { super.onResume(); myObservable .compose(bindToLifecycle()) .subscribe(); } }
  77. 77. RxLifecycle & Navi
  78. 78. Navi • https://github.com/trello/navi • better listeners for Activity/Fragment events • decoupling code from Activity/Fragment naviComponent.addListener(Event.CREATE, new Listener<Bundle>() { @Override public void call(Bundle bundle) { setContentView(R.layout.main); } } );
  79. 79. • converting lifecycle callbacks into Observables! RxNavi RxNavi .observe(naviComponent, Event.CREATE) .subscribe(bundle -> setContentView(R.layout.main));
  80. 80. RxLifecycle & Navi public class MyActivity extends NaviActivity { private final ActivityLifecycleProvider provider = NaviLifecycle.createActivityLifecycleProvider(this); }
  81. 81. RxLifecycle & Navi public class MyActivity extends NaviActivity { private final ActivityLifecycleProvider provider = NaviLifecycle.createActivityLifecycleProvider(this); @Override public void onResume() { super.onResume(); myObservable .compose(provider.bindToLifecycle()) .subscribe(…); } }
  82. 82. RxLifecycle & custom solution public class MainActivityFragment extends Fragment { BehaviorSubject<FragmentEvent> mLifecycleSubject = BehaviorSubject.create(); }
  83. 83. RxLifecycle & custom solution public class MainActivityFragment extends Fragment { BehaviorSubject<FragmentEvent> mLifecycleSubject = BehaviorSubject.create(); @Override public void onPause() { super.onPause(); Timber.i("onPause"); mLifecycleSubject.onNext(FragmentEvent.PAUSE); } }
  84. 84. public class MainActivityFragment extends Fragment { BehaviorSubject<FragmentEvent> mLifecycleSubject = BehaviorSubject.create(); @Override public void onPause() { super.onPause(); Timber.i("onPause"); mLifecycleSubject.onNext(FragmentEvent.PAUSE); } @Override public void onResume() { super.onResume(); myObservable // e.g. UI events Observable .compose( RxLifecycle .bindUntilEvent(mLifecycleSubject, FragmentEvent.PAUSE)) .doOnUnsubscribe(() -> Timber.i("onUnsubscribe")) .subscribe(…); } } RxLifecycle & custom solution
  85. 85. RxBinding
  86. 86. RxBinding RxView.clicks(vBtnSearch) .subscribe( v -> { Intent intent = new Intent(getActivity(), SearchActivity.class); startActivity(intent); } );
  87. 87. RxBinding RxTextView.textChanges(vEtSearch) .observeOn(Schedulers.io()) .flatMap(s -> mApiService.search(s.toString())) .subscribe( response -> Timber.i("Count: " + response.totalCount()) );
  88. 88. RxBinding RxTextView.textChanges(vEtSearch) .debounce(2, TimeUnit.SECONDS) .observeOn(Schedulers.io()) .flatMap(s -> mApiService.search(s.toString())) .subscribe( response -> Timber.i("Count: " + response.totalCount()) );
  89. 89. RxBinding RxTextView.textChanges(vEtSearch) .debounce(2, TimeUnit.SECONDS) .observeOn(Schedulers.io()) .flatMap(s -> mApiService.search(s.toString())) .flatMap(response -> Observable.from(response.getItems()) .subscribe( s -> Timber.i("item: " + s) );
  90. 90. Retrofit
  91. 91. Retrofit • sync or async API (Retrofit 2) @GET("group/{id}/users") Call<List<User>> groupList(@Path("id") int groupId); @GET("group/{id}/users") Observable<List<User>> groupList(@Path("id") int groupId); • reactive API
  92. 92. Retrofit • onNext() with Response, then onComplete() • onError() in case of error @GET("/data") Observable<Response> getData( @Body DataRequest dataRequest);
  93. 93. Retrofit RxTextView.textChanges(vEtSearch) .debounce(2, TimeUnit.SECONDS) .observeOn(Schedulers.io()) .flatMap(s -> mApiService.search(s.toString()) ) .flatMap(list -> Observable.from(list)) .subscribe( s -> Timber.i("item: " + s) );
  94. 94. Retrofit RxTextView.textChanges(vEtSearch) .debounce(2, TimeUnit.SECONDS) .observeOn(Schedulers.io()) .flatMap(s -> mApiService.search(s.toString()) ) .flatMap(list -> Observable.from(list)) .subscribe( s -> Timber.i("item: " + s) );
  95. 95. RxJava & SQLite • use SQLBrite • wrapper around SQLiteOpenHelper and ContentResolver SqlBrite sqlBrite = SqlBrite.create(); BriteDatabase db = sqlBrite .wrapDatabaseHelper(openHelper, Schedulers.io()); Observable<Query> users = db .createQuery("users", "SELECT * FROM users");
  96. 96. References • https://github.com/ReactiveX/RxJava • https://github.com/ReactiveX/RxAndroid • https://github.com/JakeWharton/RxRelay • https://github.com/trello/RxLifecycle • https://github.com/trello/navi • https://github.com/JakeWharton/RxBinding • https://github.com/square/retrofit • https://github.com/square/sqlbrite • advanced RxJava blog https://akarnokd.blogspot.com • http://slides.com/yaroslavheriatovych/frponandroid
  97. 97. Q&A

×