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

Practical RxJava for Android

Practical RxJava for Android

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.

Tomáš Kypta

October 08, 2016
Tweet

More Decks by Tomáš Kypta

Other Decks in Programming

Transcript

  1. What’s RxJava? • composable data flow • push concept •

    combination of • observer pattern • iterator pattern • functional programming
  2. Typical non-reactive app Event Source Views Network DB … Listener

    Listener Listener Listener logic logic logic logic State
  3. 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
  4. 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
  5. 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
  6. Java 8 & Android • use Retrolambda • or jack

    compiler • usable since build plugin 2.2.0
  7. 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
  8. 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));
  9. Key parts • Observable • Observer or Subscriber • onNext(T)

    • onCompleted() • onError(Throwable) • Subject
  10. What is RxJava good for? • making code simple and

    readable • …with Java 8 • Async processing • no AsyncTask, AsyncTaskLoader, …
  11. Async composition • Async composition • RxJava offers simple chaining

    of async operations • eliminates callback hell
  12. 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
  13. 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);
  14. 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);
  15. Observable creation • create() • better to avoid private Object

    getData() {...} public Observable<Object> getObservable() { return Observable.create(subscriber -> { subscriber.onNext(getData()); subscriber.onCompleted(); }); }
  16. Observable creation • just() private Object getData() {...} public Observable<Object>

    getObservable() { return Observable.just(getData()); } !
  17. just() & defer() • defer() private Object getData() {...} public

    Observable<Object> getObservable() { return Observable.defer( () -> Observable.just(getData()) ); }
  18. 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(); } }); }
  19. 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);
  20. RxRelay • Relay = Subject - onComplete() - onError() •

    Relay = Observable & Action1 • call() instead of onNext()
  21. Threading • Parts of a data flow can run on

    different threads! • Threading in RxJava defined by Schedulers
  22. Threading • subscribeOn(Scheduler) • subscribes to Observable on the Scheduler

    • observeOn(Scheduler) • Observable emits on the Scheduler
  23. RxLifecycle • auto unsubscribe based on Activity/Fragment lifecycle myObservable .compose(

    RxLifecycle.bindUntilEvent( lifecycleObservable, ActivityEvent.DESTROY ) ) .subscribe(…); myObservable .compose(RxLifecycle.bindActivity(lifecycleObs)) .subscribe(…);
  24. RxLifecycle • How to obtain ActivityEvent or FragmentEvent? A. rxlifecycle-components

    + subclass RxActivity, RxFragment B. Navi + rxlifecycle-navi C. Write it yourself
  25. RxLifecycle public class MyActivity extends RxActivity { @Override public void

    onResume() { super.onResume(); myObservable .compose(bindToLifecycle()) .subscribe(); } }
  26. 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); } } );
  27. RxLifecycle & Navi public class MyActivity extends NaviActivity { private

    final ActivityLifecycleProvider provider = NaviLifecycle.createActivityLifecycleProvider(this); }
  28. 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(…); } }
  29. RxLifecycle & custom solution public class MainActivityFragment extends Fragment {

    BehaviorSubject<FragmentEvent> mLifecycleSubject = BehaviorSubject.create(); }
  30. 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); } }
  31. 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
  32. RxBinding RxView.clicks(vBtnSearch) .subscribe( v -> { Intent intent = new

    Intent(getActivity(), SearchActivity.class); startActivity(intent); } );
  33. 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
  34. Retrofit • onNext() with Response, then onComplete() • onError() in

    case of error @GET("/data") Observable<Response> getData( @Body DataRequest dataRequest);
  35. 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");
  36. 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
  37. Q&A