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

Stranger Streams - How to RxAndroid

Stranger Streams - How to RxAndroid

My 2017 CodeMash talk on RxJava and RxAndroid.

Michael Yotive

January 15, 2017
Tweet

More Decks by Michael Yotive

Other Decks in Technology

Transcript

  1. STRANGER STREAMS THE ASYNC PROBLEM ▸ Keep the main thread

    clean ▸ Some tools for performing background work: ▸ Handlers ▸ Loaders ▸ AsyncTask ▸ Future ▸ EventBus
  2. EVALUATING TOOLS FOR ASYNC WORK ▸ Christina Lee - Intro

    to RxJava ▸ When do the run? ▸ How do they run? ▸ Whom do they impact? STRANGER STREAMS https://realm.io/news/intro-to-rxjava/
  3. EXAMPLE: ASYNCTASK STRANGER STREAMS public class MyAsyncTask extends AsyncTask{ //

    you may separate this or combined to caller class. public interface AsyncResponse { void processFinish(String output); } public AsyncResponse callback = null; public MyAsyncTask(AsyncResponse callback){ this.callback = callback; } @Override protected void doInBackground(Params ...) { ... } @Override protected void onPostExecute(String result) { callback.processFinish(result); } } new MyAsyncTask(myCallback).execute();
  4. EXAMPLE: ASYNCTASK STRANGER STREAMS public class MyAsyncTask extends AsyncTask{ //

    you may separate this or combined to caller class. public interface AsyncResponse { void processFinish(String output); } public AsyncResponse callback = null; public MyAsyncTask(AsyncResponse callback){ this.callback = callback; } @Override protected void doInBackground(Params ...) { ... } @Override protected void onPostExecute(String result) { callback.processFinish(result); } } new MyAsyncTask(myCallback).execute();
  5. EXAMPLE: ASYNCTASK STRANGER STREAMS public class MyAsyncTask extends AsyncTask{ //

    you may separate this or combined to caller class. public interface AsyncResponse { void processFinish(String output); } public AsyncResponse callback = null; public MyAsyncTask(AsyncResponse callback){ this.callback = callback; } @Override protected void doInBackground(Params ...) { ... } @Override protected void onPostExecute(String result) { callback.processFinish(result); } } new MyAsyncTask(myCallback).execute();
  6. EXAMPLE: ASYNCTASK STRANGER STREAMS public class MyAsyncTask extends AsyncTask{ //

    you may separate this or combined to caller class. public interface AsyncResponse { void processFinish(String output); } public AsyncResponse callback = null; public MyAsyncTask(AsyncResponse callback){ this.callback = callback; } @Override protected void doInBackground(Params ...) { ... } @Override protected void onPostExecute(String result) { callback.processFinish(result); } } new MyAsyncTask(myCallback).execute();
  7. EXAMPLE: ASYNCTASK STRANGER STREAMS https://realm.io/news/intro-to-rxjava/ ▸ When does it start?

    ▸ Manually started ▸ How does it run? ▸ Executed as a whole and you wait for it to complete. ▸ Who is impacted? ▸ Depends…
  8. IDEALS FOR DOING ASYNC WORK STRANGER STREAMS https://realm.io/news/intro-to-rxjava/ ▸ Explicit

    Execution ▸ Easy Thread Management ▸ Easily Composable/Transformable ▸ As few side effects as possible.
  9. THE BIG PICTURE STRANGER STREAMS subscription = Observable.create( ... )

    .operator( ... ) .operator( ... ) .operator( ... ) .subscribe( onNext(), 
 onComplete(),
 onError() 
 );
  10. LAMBDA NOTATION STRANGER STREAMS new Func1<String, Integer>(){ @Override public Integer

    call(String s){ return s.length(); } } (String s) -> { return s.length(); } Same As
  11. LAMBDA NOTATION STRANGER STREAMS new Func1<String, Integer>(){ @Override public Integer

    call(String s){ return s.length(); } } (String s) -> { return s.length(); } Same As
  12. OBSERVABLES STRANGER STREAMS ▸ Objects that emit values. ▸ OnNext(

    0 … ∞ ) ▸ OnError ( 0 … 1) ▸ OnComplete (0 … 1) ▸ Stops emitting values when either OnError or OnComplete is called. ▸ Cannot call both OnError and OnComplete.
  13. CREATING OBSERVABLES STRANGER STREAMS ▸ Just() ▸ Observable.Just(1) ▸ Observable.Just(1,

    2) ▸ Observable.Just(1, …, 9) ▸ Observable.Just(“Hi Mom!”)
  14. CREATING OBSERVABLES STRANGER STREAMS ▸ From() ▸ Observable.From(Arrays.asList(1, 2)) ▸

    Observable.From(new Integer[](1, 2, 3)) ▸ Observable.From(Iterable<? extends T>)
  15. CREATING OBSERVABLES STRANGER STREAMS ▸ Create<T>() ▸ Observable.Create(subscriber -> {


    subscriber.onNext(“Hello!”);
 subscriber.onNext(“From the Upside Down!”);
 subscriber.onComplete();
 }); At the beginning, you should *probably* never use this.
  16. OBSERVABLES - POP QUIZ STRANGER STREAMS ▸ What happens if

    an error occurs? Observable.Create(subscriber -> { 
 try {
 Integer i = someFunctionThatMayError();
 subscriber.onNext(i);
 }
 catch(Exception e) {
 subscriber.onError(e);
 } 
 subscriber.onComplete();
 }
 );
  17. SUBSCRIPTIONS STRANGER STREAMS ▸ A method of connecting the Observer

    (consumer of the data) to the Observable (object emitting data). ▸ Observable.just(“Eleven”)
 .subscribe( … )
  18. SUBSCRIPTIONS STRANGER STREAMS new Subscriber<T>{
 onNext(T data) { … },


    onError(Throwable e) { … },
 onComplete(){ … }
 }
  19. SUBSCRIPTIONS STRANGER STREAMS String[] names = new String[] { "Eleven",

    "Mike", "Dustin", "Will", "Lucas" };
 
 Observable.from(names)
 .subscribe( next -> { Log.i(TAG, "onNext name: " + next); }, error -> { Log.e(TAG, "onError", error); }, () -> { Log.i(TAG, "onComplete"); } );
  20. SUBSCRIPTIONS STRANGER STREAMS String[] names = new String[] { "Eleven",

    "Mike", "Dustin", "Will", "Lucas" };
 
 Observable.from(names)
 .subscribe( next -> { Log.i(TAG, "onNext name: " + next); }, error -> { Log.e(TAG, "onError", error); }, () -> { Log.i(TAG, "onComplete"); } );
  21. SUBSCRIPTIONS STRANGER STREAMS String[] names = new String[] { "Eleven",

    "Mike", "Dustin", "Will", "Lucas" };
 
 Observable.from(names)
 .subscribe( next -> { Log.i(TAG, "onNext name: " + next); }, error -> { Log.e(TAG, "onError", error); }, () -> { Log.i(TAG, "onComplete"); } );
  22. SUBSCRIPTIONS STRANGER STREAMS ▸ When you call “subscribe” on an

    Observable, you get a reference to the subscription. ▸ You may or may not need to hold onto this subscription. ▸ If you are done listening for events from the Observable, you should call “unsubscribe” on the Subscription reference.
  23. SUBSCRIPTIONS STRANGER STREAMS Observable observable = … ; Subscription subscription

    = observable.subscribe(); 
 …
 subscription.unsubscribe();
  24. OPERATORS STRANGER STREAMS ▸ Methods for manipulating or transforming streams.

    ▸ RxJava comes with tons out of the box: ▸ Transforming ▸ Filtering ▸ Combining ▸ Mathematical / Aggregation ▸ More…
  25. OPERATORS - MARBLE DIAGRAMS STRANGER STREAMS ▸ Visual Representation of

    operators and how they work.
 
 
 
 
 
 
 
 
 ▸ http://www.rxmarbles.com
  26. OPERATORS - FILTER STRANGER STREAMS Observable.just("Mike", "Dustin", "Will", "Lucas", "Eleven")

    .filter(name -> name.equals(“Eleven")) .subscribe(next-> Toast.makeText(getContext(),next,LENGTH_LONG)
 .show() );
  27. OPERATORS - FILTER STRANGER STREAMS Observable.just("Mike", "Dustin", "Will", "Lucas", "Eleven")

    .filter(name -> name.equals(“Eleven")) .subscribe(next-> Toast.makeText(getContext(),next,LENGTH_LONG)
 .show() );
  28. OPERATORS - MAP STRANGER STREAMS Observable.just("Eleven", "Mike", "Dustin", "Will", "Lucas")

    .map(s -> s.length()) .subscribe(integer -> 
 Toast.makeText(getContext(), 
 String.valueOf(integer), Toast.LENGTH_SHORT)
 .show()
 );
  29. OPERATORS - MAP STRANGER STREAMS Observable.just("Eleven", "Mike", "Dustin", "Will", "Lucas")

    .map(s -> s.length()) .subscribe(integer -> 
 Toast.makeText(getContext(), 
 String.valueOf(integer), Toast.LENGTH_SHORT)
 .show()
 );
  30. OPERATORS - MAP STRANGER STREAMS ▸ Question 1 - If

    we can convert from String to Integer, can we convert from one object to another? ▸ Yes. ▸ Question 2 - Can we convert one Observable to another? ▸ Let’s find out…
  31. OPERATORS - MAP STRANGER STREAMS public interface CodeMashAPI { @GET("api/SessionsData")

    Observable<List<Session>> GetSessions(); @GET("api/SessionsData/{id}") Observable<Session> GetSessionById(@Path("id") UUID id); @GET("api/SpeakersData") Observable<List<Speaker>> GetSpeakers(); @GET("api/SpeakersData/{id}") Observable<SpeakerDetail> GetSpeakerDetail(@Path("id") UUID id); }
  32. OPERATORS - MAP STRANGER STREAMS CodeMashAPI api = …; Observable<List<Speaker>>

    speakers = api.getSpeakerList(); speakers.map(speaker -> api.getSpeakerDetail(speaker.getId())) .subscribe(speakerDetail -> 
 displaySpeakerData(speakerDetail));
  33. OPERATORS - MAP STRANGER STREAMS CodeMashAPI api = …; Observable<List<Speaker>>

    speakers = api.getSpeakerList(); speakers.map(speaker -> api.getSpeakerDetail(speaker.getId())) .subscribe(speakerDetail -> 
 displaySpeakerData(speakerDetail));
  34. OPERATORS - MAP STRANGER STREAMS CodeMashAPI api = …; Observable<List<Speaker>>

    speakers = api.getSpeakerList(); speakers.map(speaker -> api.getSpeakerDetail(speaker.getId())) .subscribe(speakerDetail -> 
 displaySpeakerData(speakerDetail)); Observable<SpeakerDetail>
  35. OPERATORS - MAP STRANGER STREAMS CodeMashAPI api = …; Observable<List<Speaker>>

    speakers = api.getSpeakerList(); speakers.map(speaker -> api.getSpeakerDetail(speaker.getId())) .subscribe(speakerDetail -> 
 displaySpeakerData(speakerDetail));
  36. OPERATORS - FLAT MAP STRANGER STREAMS CodeMashAPI api = …;

    Observable<List<Speaker>> speakers = api.getSpeakerList(); speakers.flatMap(speaker -> api.getSpeakerDetail(speaker.getId())) .subscribe(speakerDetail -> 
 displaySpeakerData(speakerDetail));
  37. OPERATORS - FLAT MAP STRANGER STREAMS CodeMashAPI api = …;

    Observable<List<Speaker>> speakers = api.getSpeakerList(); speakers.flatMap(speaker -> api.getSpeakerDetail(speaker.getId())) .subscribe(speakerDetail -> 
 displaySpeakerData(speakerDetail)); SpeakerDetail
  38. OPERATORS - MAP VS FLAT MAP STRANGER STREAMS ▸ Map

    converts from on type to another. ▸ Flat Map transforms an observable from one type to an observable of another type.
  39. OPERATORS - ZIP STRANGER STREAMS GitHubApi api = getGitHubApi(); Observable<User>

    userObservable = 
 api.getUser(loginName);
 
 Observable<List<Events>> eventsObservable = 
 api.listEvents(loginName);
  40. OPERATORS - ZIP STRANGER STREAMS GitHubApi api = getGitHubApi(); Observable<User>

    userObservable = 
 api.getUser(loginName);
 
 Observable<List<Events>> eventsObservable = 
 api.listEvents(loginName);
  41. OPERATORS - ZIP STRANGER STREAMS Observable<User> userObservable = … ;

    Observable<List<Events>> eventsObservable = … ; Observable<UserEvents> combined = 
 Observable.zip(
 userObservable, eventsObservable, (user, events) -> new UserEvents(user, events); );
  42. OPERATORS - ZIP STRANGER STREAMS Observable<User> userObservable = … ;

    Observable<List<Events>> eventsObservable = … ; Observable<UserEvents> combined = 
 Observable.zip(
 userObservable, eventsObservable, (user, events) -> new UserEvents(user, events); );
  43. OPERATORS - ZIP STRANGER STREAMS Observable<User> userObservable = … ;

    Observable<List<Events>> eventsObservable = … ; Observable<UserEvents> combined = 
 Observable.zip(
 userObservable, eventsObservable, (user, events) -> new UserEvents(user, events); ); combined.subscribe(userEventObj ->
 // do something with UserEvents object );
  44. SCHEDULERS STRANGER STREAMS ▸ Mechanism for scheduling/managing threads. ▸ Govern

    the transition between threads. ▸ Two operators: ▸ subscribeOn ▸ observeOn
  45. SCHEDULERS - SUBSCRIBE ON STRANGER STREAMS ▸ Specifies which thread

    the Observable will operate. ▸ If not specified, defaults to the thread on which the Observable was created. ▸ Declared only once. ▸ If used multiple times, first usage wins.
  46. SCHEDULERS - OBSERVE ON STRANGER STREAMS ▸ Specifies which thread

    the Observer will operate. ▸ If not specified, defaults to the thread on which subscribeOn was assigned ▸ Declared as many times as needed ▸ If used multiple times, affects downstream operations.
  47. SCHEDULERS STRANGER STREAMS subscription = Observable
 .fromCallable( ()-> expensiveMethod() )

    .subscribeOn( Schedulers.io() ) .observeOn( Schedulers.newThread() ) .subscribe( onNext(),
 onError(),
 onComplete()
 );
  48. SCHEDULERS STRANGER STREAMS createSpeakerObservable() .subscribeOn(Schedulers.io()) .observeOn(<Android Main UI Thread>) .subscribe(new

    Subscriber<List<Speaker>>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { Log.e(TAG, "Error calling CodeMash api", e); } @Override public void onNext(List<Speaker> speakers) { Toast.makeText(getContext(), "Number of speakers: " + String.valueOf(speakers.size()), Toast.LENGTH_LONG).show(); } });
  49. SCHEDULERS STRANGER STREAMS createSpeakerObservable() .subscribeOn(Schedulers.io()) .observeOn(<Android Main UI Thread>) .subscribe(new

    Subscriber<List<Speaker>>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { Log.e(TAG, "Error calling CodeMash api", e); } @Override public void onNext(List<Speaker> speakers) { Toast.makeText(getContext(), "Number of speakers: " + String.valueOf(speakers.size()), Toast.LENGTH_LONG).show(); } });
  50. RXANDROID STRANGER STREAMS ▸ An extension to RxJava built just

    for Android. ▸ Adds the bare minimum classes needed to do RxJava on Android. ▸ AndroidSchedulers.mainThread()
  51. RXANDROID - CAVEATS STRANGER STREAMS ▸ Subscriptions and the Android

    lifecycle ▸ How do we continue a subscription on configuration change? ▸ Be mindful if you have a long running operation and your activity/fragment is terminated.
  52. RXANDROID - CAVEATS STRANGER STREAMS public class MainActivity extends AppCompatActivity

    { private Subscription subscription; private Observable observable; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... observable = <create observable here>; } @Override protected void onResume() { super.onResume(); subscription = observable.subscribe(); } @Override protected void onStop() { super.onDestroy(); subscription.unsubscribe(); } }
  53. RXANDROID - CAVEATS STRANGER STREAMS public class MainActivity extends AppCompatActivity

    { private Subscription subscription; private Observable observable; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... observable = <create observable here>; } @Override protected void onResume() { super.onResume(); subscription = observable.subscribe(); } @Override protected void onStop() { super.onDestroy(); subscription.unsubscribe(); } }
  54. RXANDROID - CAVEATS STRANGER STREAMS private CompositeSubscription compositeSubscription; private Observable

    observable1, observable2, observable3; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); observable1 = <create observable here>; observable2 = <create observable here>; observable3 = <create observable here>; } @Override protected void onResume() { super.onResume(); compositeSubscription.add(observable1.subscribe(); compositeSubscription.add(observable2.subscribe(); compositeSubscription.add(observable3.subscribe(); } @Override protected void onStop() { super.onStop(); compositeSubscription.unsubscribe(); }
  55. RXANDROID - CAVEATS STRANGER STREAMS private CompositeSubscription compositeSubscription; private Observable

    observable1, observable2, observable3; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); observable1 = <create observable here>; observable2 = <create observable here>; observable3 = <create observable here>; } @Override protected void onResume() { super.onResume(); compositeSubscription.add(observable1.subscribe(); compositeSubscription.add(observable2.subscribe(); compositeSubscription.add(observable3.subscribe(); } @Override protected void onStop() { super.onStop(); compositeSubscription.unsubscribe(); }
  56. RXANDROID - RXBINDING STRANGER STREAMS ▸ Adds bindings to Android

    UI widgets. RxTextView.textChanges(<view>)
 .map( c -> … )
 .subscribe( i -> … )
  57. RXANDROID - RXLIFECYCLE STRANGER STREAMS ▸ Adds bindings to respond

    to the Android Lifecycle myObservable.compose(bindUntilEvent(ActivityEvent.Destroy))
 .subscribe( … ) (Note: Fragments/Activities must extend RxActivity/
 RxAppCompatActivity or RxFragment)
  58. CLOSING THOUGHTS STRANGER STREAMS ▸ Observables are immutable z Observable<String>

    obs = Observable.just(“Hello”); obs.map(s -> s.length()); obs.subscribe( i -> Log.i(TAG, String.valueOf(i)); versus Observable.just(“Hello”) .map(s -> s.length()) .subscribe(i -> Log.i(TAG, String.valueOf(i));
  59. CLOSING THOUGHTS STRANGER STREAMS ▸ Observables are immutable z Observable<String>

    obs = Observable.just(“Hello”); obs.map(s -> s.length()); obs.subscribe( i -> Log.i(TAG, String.valueOf(i)); versus Observable.just(“Hello”) .map(s -> s.length()) .subscribe(i -> Log.i(TAG, String.valueOf(i));
  60. CLOSING THOUGHTS STRANGER STREAMS ▸ Observables are immutable z Observable<String>

    obs1 = Observable.just(“Hello”); Observable<Integer> obs2 = obs1.map(s -> s.length()); obs2.subscribe( i -> Log.i(TAG, String.valueOf(i));
  61. CLOSING THOUGHTS STRANGER STREAMS ▸ Observables are immutable z Observable<String>

    obs1 = Observable.just(“Hello”); Observable<Integer> obs2 = obs1.map(s -> s.length()); obs2.subscribe( i -> Log.i(TAG, String.valueOf(i));
  62. CLOSING THOUGHTS STRANGER STREAMS ▸ RxJava 2.0 ▸ Released October

    29th ▸ New Type: Flowable ▸ Use cases: https://goo.gl/HzLFfm ▸ Nulls are no longer permitted. z Observable.just(null); //don’t do this
  63. CLOSING THOUGHTS STRANGER STREAMS ▸ Helpful Libraries that aid in

    reactive development on Android ▸ Rx Cache ▸ Rx Preferences ▸ RxLoader ▸ RxPermissions ▸ Retrofit ▸ Realm z