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.

GKAC 2015 Apr. - RxAndroid

1,292 views

Published on

Speaker: 김용욱

GDG Korea Android Conference 2015 Apr.
Google Campus Seoul
2015. 4. 18

Published in: Software
  • Be the first to comment

GKAC 2015 Apr. - RxAndroid

  1. 1. RxAndroid Leo Y. Kim (@dalinaum)
  2. 2. Rx The Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators. https://msdn.microsoft.com/en-us/data/gg577609
  3. 3. RxJava RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences. Copyright 2013 Netflix, Inc. (https://github.com/ReactiveX/RxJava)
  4. 4. RxAndroid
  5. 5. RxAndroid
  6. 6. build.gradle dependencies { ... compile 'io.reactivex:rxandroid:0.24.0' }
  7. 7. Observable and Subscriber River Observable onNext onNext onCompleted
  8. 8. onNext An Observable calls this method whenever the Observable emits an item. This method takes as a parameter the item emitted by the Observable.
  9. 9. onError An Observable calls this method to indicate that it has failed to generate the expected data or has encountered some other error. This stops the Observable and it will not make further calls to onNext or onCompleted. The onError method takes as its parameter an indication of what caused the error.
  10. 10. onCompleted An Observable calls this method after it has called onNext for the final time, if it has not encountered any errors.
  11. 11. 3 methods
  12. 12. Simple Observable Observable<String> simpleObservable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { subscriber.onNext("Hello RxAndroid !!"); subscriber.onCompleted(); } });
  13. 13. Simple(?) Subscriber simpleObservable .subscribe(new Subscriber<String>() { @Override public void onCompleted() { Log.d(TAG, "complete!"); } (cont.)
  14. 14. Subscriber @Override public void onError(Throwable e) { Log.e(TAG, "error: " + e.getMessage()); } @Override public void onNext(String text) { ((TextView) findViewById(R.id.textView)).setText(text); } });
  15. 15. Is it simple subsciber? Not yet...
  16. 16. simpleObservable .subscribe(new Action1<String>() { @Override public void call(String text) { ((TextView) findViewById(R.id.textView)).setText(text); } }); More simple subscriber #1
  17. 17. simpleObservable .subscribe(new Action1<String>() { ... }, new Action1<Throwable>() { ... }, new Action0() { ... }); More simple subscriber #2
  18. 18. simpleObservable .subscribe(new Action1<String>() { ... }, new Action1<Throwable>() { ... }); More simple subscriber #3
  19. 19. Operator: Map
  20. 20. Operator: Map .map(new Func1<Integer, Integer>() { @Override public Integer call(Integer value) { return value * 10; } })
  21. 21. Operator: map simpleObservable .map(new Func1<String, String>() { @Override public String call(String text) { return text.toUpperCase(); } }) .subscribe(new Action1<String>() { @Override public void call(String text) { ((TextView) findViewById(R.id.textView)).setText(text); } });
  22. 22. Operator: map simpleObservable .map(new Func1<String, Integer>() { @Override public Integer call(String text) { return text.length(); } }) .subscribe(new Action1<Integer>() { @Override public void call(Integer length) { ((TextView) findViewById(R.id.textView)).setText("length: " + length); } });
  23. 23. More operators http://reactivex.io/documentation/operators.html
  24. 24. Observer utilities Observable<String> simpleObservable = Observable.just("Hello RxAndroid"); see also: Observable.from (for collections)
  25. 25. Lambda Observable<String> simpleObservable = Observable.just("Hello Lambda!!"); simpleObservable .map(text -> text.length()) .subscribe(length ->((TextView)findViewById(R.id.textView)) .setText("length: " + length));
  26. 26. What is that? Java 8 Lambda
  27. 27. Lambda: stage 1 .map(new Func1<String, Integer>() { @Override public Integer call(String text) { return text.length(); } })
  28. 28. Lambda: stage 2 .map((String text) -> { return text.length(); })
  29. 29. Lambda: stage 3 .map((text) -> { return text.length(); })
  30. 30. Lambda: stage 4 .map(text -> { return text.length(); })
  31. 31. Lambda: stage 5 .map(text -> text.length() )
  32. 32. Wait? Java 8!?! Java 8 Lambda
  33. 33. Retrolambda Java 8 Lambda
  34. 34. buildscript { repositories { jcenter() } dependencies { classpath 'me.tatarka:gradle-retrolambda:2.5.0' } } apply plugin: 'com.android.application' apply plugin: 'me.tatarka.retrolambda' build.gradle
  35. 35. android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } … } build.gradle
  36. 36. RxAndroid: ViewObservable.clicks
  37. 37. ViewObservable.clicks ViewObservable .clicks(findViewById(R.id.button)) .map(event -> new Random().nextInt()) .subscribe(value -> { TextView textView = (TextView) findViewB yId(R.id.textView); textView.setText("number: " + value.toString()); }, throwable -> { Log.e(TAG, "Error: " + throwable.getMessage()); throwable.printStackTrace(); });
  38. 38. Observable<Event> ViewObservable .clicks(findViewById(R.id.button)) .map(event -> new Random().nextInt()) .subscribe(value -> { TextView textView = (TextView) findViewById(R.id.textView); textView.setText("number: " + value.toString()); }, throwable -> { Log.e(TAG, "Error: " + throwable.getMessage()); throwable.printStackTrace(); });
  39. 39. Map: Event -> Integer ViewObservable .clicks(findViewById(R.id.button)) .map(event -> new Random().nextInt()) .subscribe(value -> { TextView textView = (TextView) findViewById(R.id.textView); textView.setText("number: " + value.toString()); }, throwable -> { Log.e(TAG, "Error: " + throwable.getMessage()); throwable.printStackTrace(); });
  40. 40. Subscriber ViewObservable .clicks(findViewById(R.id.button)) .map(event -> new Random().nextInt()) .subscribe(value -> { TextView textView = (TextView) findViewById(R.id.textView); textView.setText("number: " + value.toString()); }, throwable -> { Log.e(TAG, "Error: " + throwable.getMessage()); throwable.printStackTrace(); });
  41. 41. Observable.merge
  42. 42. Merge
  43. 43. Observable.merge Observable<String> lefts = ViewObservable.clicks(findViewById(R.id.leftButton)) .map(event -> "left"); Observable<String> rights = ViewObservable.clicks(findViewById(R.id. rightButton)) .map(event -> "right"); Observable<String> together = Observable.merge(lefts, rights); together.subscribe(text -> ((TextView) findViewById(R.id.textView)).setText(text)); together.map(text -> text.toUpperCase()) .subscribe(text -> Toast.makeText(this, text, Toast.LENGTH_SHORT).show());
  44. 44. 2 Observables Observable<String> lefts = ViewObservable.clicks(findViewById(R.id.leftButton)) .map(event -> "left"); Observable<String> rights = ViewObservable.clicks(findViewById(R.id. rightButton)) .map(event -> "right"); Observable<String> together = Observable.merge(lefts, rights); together.subscribe(text -> ((TextView) findViewById(R.id.textView)).setText(text)); together.map(text -> text.toUpperCase()) .subscribe(text -> Toast.makeText(this, text, Toast.LENGTH_SHORT).show());
  45. 45. Observable.merge Observable<String> lefts = ViewObservable.clicks(findViewById(R.id.leftButton)) .map(event -> "left"); Observable<String> rights = ViewObservable.clicks(findViewById(R.id. rightButton)) .map(event -> "right"); Observable<String> together = Observable.merge(lefts, rights); together.subscribe(text -> ((TextView) findViewById(R.id.textView)).setText(text)); together.map(text -> text.toUpperCase()) .subscribe(text -> Toast.makeText(this, text, Toast.LENGTH_SHORT).show());
  46. 46. 2 Subscriber Observable<String> lefts = ViewObservable.clicks(findViewById(R.id.leftButton)) .map(event -> "left"); Observable<String> rights = ViewObservable.clicks(findViewById(R.id. rightButton)) .map(event -> "right"); Observable<String> together = Observable.merge(lefts, rights); together.subscribe(text -> ((TextView) findViewById(R.id.textView)).setText(text)); together.map(text -> text.toUpperCase()) .subscribe(text -> Toast.makeText(this, text, Toast.LENGTH_SHORT).show());
  47. 47. Operator: scan
  48. 48. Operator: scan Observable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1); Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1); Observable<Integer> together = Observable.merge(minuses, pluses); together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString())); together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
  49. 49. 2 Observables (+map) Observable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1); Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1); Observable<Integer> together = Observable.merge(minuses, pluses); together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString())); together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
  50. 50. Merge Observable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1); Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1); Observable<Integer> together = Observable.merge(minuses, pluses); together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString())); together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
  51. 51. 1st scan + subscriber: counter Observable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1); Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1); Observable<Integer> together = Observable.merge(minuses, pluses); together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString())); together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
  52. 52. 2nd scan + subscriber: sum Observable<Integer> minuses = ViewObservable.clicks(findViewById(R.id.minusButton)) .map(event -> -1); Observable<Integer> pluses = ViewObservable.clicks(findViewById(R.id.plusButton)) .map(event -> 1); Observable<Integer> together = Observable.merge(minuses, pluses); together.scan(0, (sum, number) -> sum + 1) .subscribe(count -> ((TextView) findViewById(R.id.count)).setText(count.toString())); together.scan(0, (sum, number) -> sum + number) .subscribe(number -> ((TextView) findViewById(R.id.number)).setText(number.toString()));
  53. 53. Scheduler
  54. 54. Scheduler Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(/* an Observer */);
  55. 55. Observable runs on “newThread” Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(/* an Observer */);
  56. 56. Subscriber runs on “mainThread” Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(/* an Observer */); Notice: Subscriber doesn’t run on mainThread immediately.
  57. 57. Bound new Thread(() -> { final Handler handler = new Handler(); Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.handlerThread(handler)) .subscribe(/* an Observer */) }, "custom-thread-1").start();
  58. 58. Bound to this thread new Thread(() -> { final Handler handler = new Handler(); Observable.from("one", "two", "three", "four", "five") .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.handlerThread(handler)) .subscribe(/* an Observer */) }, "custom-thread-1").start();
  59. 59. Varieties of Scheduler (RxJava) ● Schedulers.computation( ) ● Schedulers.from(executor) ● Schedulers.immediate( ) ● Schedulers.io( ) ● Schedulers.newThread( ) ● Schedulers.trampoline( )
  60. 60. Varieties of Scheduler (RxAndroid) ● AndroidSchedulers.mainThread() ● AndroidSchedulers.handlerThread(handler)
  61. 61. Use Schedulers without Observable worker = Schedulers.newThread().createWorker(); worker.schedule(() -> { yourWork(); });
  62. 62. Unsubscirbe Subscription Subscription s = Observable.from("one", "two", "three", "four", "five").subscribe(/* Observer */) s.unsubscribe()
  63. 63. Unsubscirbe CompositeSubscription private void doSomething() { mCompositeSubscription.add( s.subscirbe(/* observer*/); } @Override protected void onDestroy() { super.onDestroy(); mCompositeSubscription.unsubscribe(); }
  64. 64. Unsubscirbe Subscription @Override protected void onDestroy() { super.onDestroy(); s.unsubscribe(); }
  65. 65. Recursive Schedulers worker = Schedulers.newThread().createWorker(); worker.schedule(() { yourWork(); worker.schedule(this); }); // It will stop your worker worker.unsubscribe();
  66. 66. Retrofit with RxAndroid
  67. 67. Retrofit without RxAndroid @GET(“/user/{id}”) void getUser( @path(“id”) long id, Callback<User> callback);
  68. 68. Retrofit with RxAndroid @GET(“/user/{id}”) Observable<User> getUser( @path(“id”) long id);
  69. 69. You can combie Retrofit’s Observables Observable.zip( service.getUserPhoto(id), service.getPhotoMetadata(id), (photo, metadata) -> createPhotoWithData(photo, metadata)) .subscribe(photoWithData -> showPhoto(photoWithData));
  70. 70. Battery with RxAndroid https://github.com/spoqa/battery
  71. 71. Battery with RxAndroid @RpcObject(uri="http://ip.jsontest.com/") public class JsonTest { @Response public String ip; } Observable<JsonTest> jsonTestObservable = Rpc.invokeRx(new AndroidExecutionContext(this), jsonTest); jsonTestObservable.subscribe(jsonText -> { Log.d("MAIN", jsonTest.ip); });
  72. 72. Subject
  73. 73. Subject A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable.
  74. 74. PublishSubject PublishSubject publishSubject<View> = PublishSubject.create(); view.setOnClickListener(view -> publishSubject.onNext(view)); Observable<View> observable = publishSubject.asObservable();
  75. 75. Use ReplaySubject for caching ReplaySubject<ImageSearchResult> mSubject = ReplaySubject.create(); mSubject = ReplaySubject.create(); mPageNumber = 1; mApi.searchImage( "json", API_KEY, query, ITEM_COUNT, mPageNumber) .observeOn(AndroidSchedulers.mainThread()) .subscribe(mSubject::onNext);
  76. 76. Q&A
  77. 77. See also https://github.com/GDG-Korea/HelloRx https://github.com/hl5pma/RxExample

×