RxApplications

 RxApplications

A talk that gets into the basics of using RxJava for observing asynchronous events including user input, network responses, and the result of database interactions.

A2ca9534b32c7dbf57b3142ad9884f15?s=128

Kevin Carpenter

April 22, 2017
Tweet

Transcript

  1. RxApplications Kevin Carpenter Notion AI

  2. Application behavior is asynchronous.

  3. Pull to Refresh

  4. GET https://hacker-news.firebaseio.com/v0/items [{ "by": "jamesblonde", "descendants": 0, "id": 11595667, "score":

    2, "time": 1461937655, "title": “Make email great again”, "type": "story", "url": “https:\/\/email.net/great” }]
  5. INSERT INTO items (title, author, points, date)
 VALUES (‘Rx’, ‘kevcar’,

    90, 1492278237562)
  6. DELETE FROM items WHERE date < 1492278000 AND date >

    1492280000
  7. None
  8. None
  9. ☝ swipeRefreshLayout.setOnRefreshListener( new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() {

    // make update request } } );
  10. RxJava

  11. RxJava RxJS

  12. RxJava RxJS RxKotlin

  13. RxJava RxJS RxKotlin RxSwift

  14. “ReactiveX is a combination of the best ideas from the

    Observer pattern, the Iterator pattern, and functional programming”
  15. abstract class Observer<T> { 
 fun update(T notification) 
 }

  16. abstract class Observable<T> { 
 private val ArrayList<Observer<T>> observers; 


    
 fun registerObserver(Observer<T> observer) { 
 observers.add(observer); 
 } 
 
 fun unregisterObserver(Observer<T> observer) { 
 observers.remove(observer); 
 } 
 } 

  17. abstract class Observable<T> { 
 private val observers: ArrayList<Observer<T>> 


    
 fun registerObserver(observer: Observer<T>) { 
 observers.add(observer); 
 } 
 
 fun unregisterObserver(observer: Observer<T>) { 
 observers.remove(observer); 
 } 
 } 

  18. abstract class Observable<T> { 
 private val observers: ArrayList<Observer<T>>
 


    void registerObserver(observer: Observer<T>) { 
 observers.add(observer) 
 } 
 
 void unregisterObserver(observer: Observer<T>) { 
 observers.remove(observer);
 } 
 
 void setValue(newValue: T) { 
 this.value = newValue
 notifyObservers(newValue)
 } 
 } 

  19. abstract class Observable<T> { 
 private val observers: ArrayList<Observer<T>> ;

    
 
 fun registerObserver(observer: Observer<T>) { 
 observers.add(observer); 
 } 
 
 fun unregisterObserver(observer: Observer<T>) { 
 observers.remove(observer); 
 } 
 
 fun setValue(newValue: T) { 
 this.value = newValue; 
 notifyObservers(newValue); 
 } 
 
 fun notifyObservers(value: T) { 
 for (observer : observers) { 
 observer.update(value); 
 } 
 } 
 }
  20. “ReactiveX is a combination of the best ideas from the

    Observer pattern, the Iterator pattern, and functional programming”
  21. interface Iterable<T> 
 
 fun iterator(): Iterator<T> 
 
 }

  22. interface Iterator<T> 
 
 fun hasNext(): Boolean 
 
 fun

    next(): T 
 
 }
  23. val sounds: Iterable<String> = listOf( "moo", "cluck", “quack" ) 


    
 while (sounds.iterator().hasNext()) { 
 log.d("Animal Sound", sounds.next()) 
 }
  24. val sounds: Iterable<String> = listOf( "moo", "cluck", “quack" ) 


    
 while (sounds.iterator().hasNext()) { 
 log.d("Animal Sound", sounds.next()) 
 } D/Animal Sound: moo
  25. val sounds: Iterable<String> = listOf( "moo", "cluck", “quack" ) 


    
 while (sounds.iterator().hasNext()) { 
 log.d("Animal Sound", sounds.next()) 
 } D/Animal Sound: moo D/Animal Sound: cluck
  26. val sounds: Iterable<String> = listOf( "moo", "cluck", “quack" ) 


    
 while (sounds.iterator().hasNext()) { 
 log.d("Animal Sound", sounds.next()) 
 } D/Animal Sound: moo D/Animal Sound: cluck D/Animal Sound: quack

  27. val sounds: Iterable<String> = listOf( "moo", "cluck", “quack" ) 


    
 sounds.map { sound -> 
 sound + sound // "$sound $sound" 
 } 
 .map { doubleSound -> 
 log.d("Animal Sound", doubleSound) 
 }
  28. val sounds: Iterable<String> = listOf( "moo", "cluck", “quack" ) 


    
 sounds.map { sound -> 
 sound + sound // "$sound $sound" 
 } 
 .map { doubleSound -> 
 log.d("Animal Sound", doubleSound) 
 } D/Animal Sound: moo moo

  29. val sounds: Iterable<String> = listOf( "moo", "cluck", “quack" ) 


    
 sounds.map { sound -> 
 sound + sound // "$sound $sound" 
 } 
 .map { doubleSound -> 
 log.d("Animal Sound", doubleSound) 
 } D/Animal Sound: moo moo D/Animal Sound: cluck cluck
  30. val sounds: Iterable<String> = listOf( "moo", "cluck", “quack" ) 


    
 sounds.map { sound -> 
 sound + sound // "$sound $sound" 
 } 
 .map { doubleSound -> 
 log.d("Animal Sound", doubleSound) 
 } D/Animal Sound: moo moo D/Animal Sound: cluck cluck D/Animal Sound: quack quack

  31. “ReactiveX is a combination of the best ideas from the

    Observer pattern, the Iterator pattern, and functional programming”
  32. f(x) = y

  33. { x -> x * x; }

  34. int square(int x) { 
 int square = x *

    x; 
 textView.text = square; 
 return result; 
 } 

  35. int square(int x) { 
 int square = x *

    x; 
 this.state.latestSquare = square; 
 return result; 
 } 

  36. // 1, 2, 3
 Observable<Integer> intObservable = Observable.range(1, 4) 


    
 intObservable.map { x -> 
 x * x 
 } 
 .map { squared -> 
 "Result: " + squared 
 } 
 .map { str -> 
 log.d(str) 
 }
  37. Observable

  38. time

  39. time

  40. time

  41. Observable.from(listOf<String>("moo", "cluck", "quack")) time

  42. Observable.from(listOf<String>("moo", "cluck", "quack")) time moo

  43. Observable.from(listOf<String>("moo", "cluck", "quack")) time moo cluck

  44. Observable.from(listOf<String>("moo", "cluck", "quack")) time moo cluck quack

  45. time moo cluck X

  46. time moo X

  47. time moo cluck

  48. Observable.subscribe( Subscriber<T> subscriber )

  49. abstract class Subscriber<T> { 
 onNext(T t); 
 onError(Throwable e);

    // Terminal 
 onCompleted(); // Terminal 
 } 

  50. abstract class Subscriber<T> { 
 onNext(T t); 
 onError(Throwable e);

    // Terminal 
 onCompleted(); // Terminal 
 } 
 
 Observable.subscribe( 
 Action1<T> onNext, 
 Action1<Throwable>, onError, 
 Action0 onCompleted) 

  51. val action = Action1<String> { 
 fun event (event: String)

    { 
 log.d("Animal Sound", event) 
 } 
 } 
 
 val errorAction = Action1<Throwable> { 
 fun event (event: Throwable) { 
 log.e(event, "Error in Animal Sounds Example") 
 } 
 } 
 
 val completedAction = Action0 { 
 fun call { 
 log.d("Completed Event", "Animal Sounds are done.") 
 } 
 } 
 
 observable.subscribe(action, errorAction, completedAction)
  52. val action = Action1<String> { 
 fun event (event: String)

    { 
 log.d("Animal Sound", event) 
 } 
 } 
 
 val errorAction = Action1<Throwable> { 
 fun event (event: Throwable) { 
 log.e(event, "Error in Animal Sounds Example") 
 } 
 } 
 
 val completedAction = Action0 { 
 fun call { 
 log.d("Completed Event", "Animal Sounds are done.") 
 } 
 } 
 
 observable.subscribe(action, errorAction, completedAction)
  53. val action = Action1<String> { 
 fun event (event: String)

    { 
 log.d("Animal Sound", event) 
 } 
 } 
 
 val errorAction = Action1<Throwable> { 
 fun event (event: Throwable) { 
 log.e(event, "Error in Animal Sounds Example") 
 } 
 } 
 
 val completedAction = Action0 { 
 fun call { 
 log.d("Completed Event", "Animal Sounds are done.") 
 } 
 } 
 
 observable.subscribe(action, errorAction, completedAction)
  54. val action = Action1<String> { 
 fun event (event: String)

    { 
 log.d("Animal Sound", event) 
 } 
 } 
 
 val errorAction = Action1<Throwable> { 
 fun event (event: Throwable) { 
 log.e(event, "Error in Animal Sounds Example") 
 } 
 } 
 
 val completedAction = Action0 { 
 fun call { 
 log.d("Completed Event", "Animal Sounds are done.") 
 } 
 } 
 
 observable.subscribe(action, errorAction, completedAction)
  55. Observable.from(listOf<String>("moo", "cluck", “quack")) time

  56. Observable.from(listOf<String>("moo", "cluck", “quack")) time moo D/Animal Sound: moo

  57. Observable.from(listOf<String>("moo", "cluck", “quack")) time moo cluck D/Animal Sound: moo D/Animal

    Sound: cluck
  58. Observable.from(listOf<String>("moo", "cluck", “quack")) time moo cluck quack D/Animal Sound: moo

    D/Animal Sound: cluck D/Animal Sound: quack
  59. Observable.from(listOf<String>("moo", "cluck", “quack")) time moo cluck quack D/Animal Sound: moo

    D/Animal Sound: cluck D/Animal Sound: quack D/Completed Event: Animal Sounds are Done
  60. Observable.from(listOf<String>("moo", "cluck", “quack")) time moo cluck D/Animal Sound: moo D/Animal

    Sound: cluck E/Error Event: Error in Animal Sounds example at com.rxapplications.example.ExampleClass… X
  61. Non-RxViews

  62. Pull to Refresh

  63. class ItemListView : FrameLayout, IItemListView { 
 
 @Inject 


    private val model: ItemListViewModel 
 
 private val refreshView by bindView<SwipeRefreshLayout>(R.id.swipe_view) 
 private val adapter = ItemListViewAdapter(context) 
 
 override fun onFinishInflate() { 
 super.onFinishInflate() 
 refreshView.setOnRefreshListener(listener) 
 } 
 
 override fun onAttachedToWindow() { 
 super.onAttachedToWindow() 
 model.attach(this) 
 } 
 
 override fun onDetachedFromWindow() { 
 model.detach(this) 
 super.onDetachedFromWindow() 
 } 
 
 private val listener = object : SwipeRefreshLayout.OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } }
  64. class ItemListView : FrameLayout, IItemListView { 
 
 @Inject 


    private val model: ItemListViewModel 
 
 private val refreshView by bindView<SwipeRefreshLayout>(R.id.swipe_view) 
 private val adapter = ItemListViewAdapter(context) 
 
 override fun onFinishInflate() { 
 super.onFinishInflate() 
 refreshView.setOnRefreshListener(listener) 
 } 
 
 override fun onAttachedToWindow() { 
 super.onAttachedToWindow() 
 model.attach(this) 
 } 
 
 override fun onDetachedFromWindow() { 
 model.detach(this) 
 super.onDetachedFromWindow() 
 } 
 
 private val listener = object : SwipeRefreshLayout.OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } }
  65. class ItemListView : FrameLayout, IItemListView { 
 
 @Inject 


    private val model: ItemListViewModel 
 
 private val refreshView by bindView<SwipeRefreshLayout>(R.id.swipe_view) 
 private val adapter = ItemListViewAdapter(context) 
 
 override fun onFinishInflate() { 
 super.onFinishInflate() 
 refreshView.setOnRefreshListener(listener) 
 } 
 
 override fun onAttachedToWindow() { 
 super.onAttachedToWindow() 
 model.attach(this) 
 } 
 
 override fun onDetachedFromWindow() { 
 model.detach(this) 
 super.onDetachedFromWindow() 
 } 
 
 private val listener = object : SwipeRefreshLayout.OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } }
  66. class ItemListView : FrameLayout, IItemListView { 
 
 @Inject 


    private val model: ItemListViewModel 
 
 private val refreshView by bindView<SwipeRefreshLayout>(R.id.swipe_view) 
 private val adapter = ItemListViewAdapter(context) 
 
 override fun onFinishInflate() { 
 super.onFinishInflate() 
 refreshView.setOnRefreshListener(listener) 
 } 
 
 override fun onAttachedToWindow() { 
 super.onAttachedToWindow() 
 model.attach(this) 
 } 
 
 override fun onDetachedFromWindow() { 
 model.detach(this) 
 super.onDetachedFromWindow() 
 } 
 
 private val listener = object : SwipeRefreshLayout.OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } }
  67. class ItemListView : FrameLayout, IItemListView { 
 // Set up


    
 override fun onFinishInflate() { 
 super.onFinishInflate() 
 refreshView.setOnRefreshListener(listener) 
 } 
 
 override fun onAttachedToWindow() { 
 super.onAttachedToWindow() 
 model.attach(this) 
 } 
 
 override fun onDetachedFromWindow() { 
 model.detach(this) 
 super.onDetachedFromWindow() 
 } }
  68. class ItemListView : FrameLayout, IItemListView { 
 // Set up


    
 override fun onFinishInflate() { 
 super.onFinishInflate() 
 refreshView.setOnRefreshListener(listener) 
 } 
 
 override fun onAttachedToWindow() { 
 super.onAttachedToWindow() 
 model.attach(this) 
 } 
 
 override fun onDetachedFromWindow() { 
 model.detach(this) 
 super.onDetachedFromWindow() 
 } }
  69. class ItemListView : FrameLayout, IItemListView { 
 // Set up


    
 override fun onFinishInflate() { 
 super.onFinishInflate() 
 refreshView.setOnRefreshListener(listener) 
 } 
 
 override fun onAttachedToWindow() { 
 super.onAttachedToWindow() 
 model.attach(this) 
 } 
 
 override fun onDetachedFromWindow() { 
 model.detach(this) 
 super.onDetachedFromWindow() 
 } }
  70. interface IItemListView { 
 
 fun onItemsResponse(items: List<Item>) 
 


    fun onItemsError(error: Throwable) 
 
 fun onRefreshComplete() 
 } 
 

  71. interface IItemListView { 
 
 fun onItemsResponse(items: List<Item>) 
 


    fun onItemsError(error: Throwable) 
 
 fun onRefreshComplete() 
 } 
 

  72. interface IItemListView { 
 
 fun onItemsResponse(items: List<Item>) 
 


    fun onItemsError(error: Throwable) 
 
 fun onRefreshComplete() 
 } 
 

  73. class ItemListView : FrameLayout, IItemListView { 
 // Set up


    
 override fun onFinishInflate() { 
 super.onFinishInflate() 
 refreshView.setOnRefreshListener(listener) 
 } 
 
 override fun onAttachedToWindow() { 
 super.onAttachedToWindow() 
 model.attach(this) 
 } 
 
 override fun onDetachedFromWindow() { 
 model.detach(this) 
 super.onDetachedFromWindow() 
 } private val listener = object : OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } }
  74. class ItemListView : FrameLayout, IItemListView { 
 // Set up


    
 override fun onFinishInflate() { 
 super.onFinishInflate() 
 refreshView.setOnRefreshListener(listener) 
 } 
 
 override fun onAttachedToWindow() { 
 super.onAttachedToWindow() 
 model.attach(this) 
 } 
 
 override fun onDetachedFromWindow() { 
 model.detach(this) 
 super.onDetachedFromWindow() 
 } private val listener = object : OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } }
  75. class ItemListView : FrameLayout, IItemListView { 
 
 private val

    listener = object : OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } 
 
 override fun onItemsResponse(items: List<Item>) { 
 adapter.setItems(items) 
 } 
 
 override fun onItemsError(error: Throwable) { 
 log.e(error, "Error in ItemListView") 
 } 
 
 override fun onRefreshComplete() { 
 refreshView.setRefreshing(false) 
 } 
 
 }
  76. class ItemListView : FrameLayout, IItemListView { 
 
 private val

    listener = object : OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } 
 
 override fun onItemsResponse(items: List<Item>) { 
 adapter.setItems(items) 
 } 
 
 override fun onItemsError(error: Throwable) { 
 log.e(error, "Error in ItemListView") 
 } 
 
 override fun onRefreshComplete() { 
 refreshView.setRefreshing(false) 
 } 
 
 }
  77. class ItemListView : FrameLayout, IItemListView { 
 
 private val

    listener = object : OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } 
 
 override fun onItemsResponse(items: List<Item>) { 
 adapter.setItems(items) 
 } 
 
 override fun onItemsError(error: Throwable) { 
 log.e(error, "Error in ItemListView") 
 } 
 
 override fun onRefreshComplete() { 
 refreshView.setRefreshing(false) 
 } 
 
 }
  78. class ItemListView : FrameLayout, IItemListView { 
 
 private val

    listener = object : OnRefreshListener { 
 override fun onRefresh() { 
 model.requestRefresh() 
 } 
 } 
 
 override fun onItemsResponse(items: List<Item>) { 
 adapter.setItems(items) 
 } 
 
 override fun onItemsError(error: Throwable) { 
 log.e(error, "Error in ItemListView") 
 } 
 
 override fun onRefreshComplete() { 
 refreshView.setRefreshing(false) 
 } 
 
 }
  79. interface IItemListView { 
 
 fun onItemsResponse(items: List<Item>) fun onItemsError(error:

    Throwable)
 
 fun onRefreshComplete()
 } 
 

  80. interface IItemListView { 
 
 fun onItemsResponse(items: List<Item>) fun onItemsError(error:

    Throwable)
 
 fun onRefreshComplete() fun onNextResponsePage(items: List<Item>) fun onNextResponsePageError(error: Throwable)
 
 fun onNextResponsePageComplete()
 } 
 

  81. RxViews

  82. Pull to Refresh

  83. class ItemListView : FrameLayout { 
 
 @Inject 
 private

    val model: ItemListViewModel 
 private val subscriptions = CompositeSubscription() 
 
 private val refreshView by bindView<SwipeRefreshLayout>(R.id.swipe) 
 private val adapter = ItemListViewAdapter(context) 
 } 
 

  84. class ItemListView : FrameLayout { 
 
 @Inject 
 private

    val model: ItemListViewModel 
 private val subscriptions = CompositeSubscription() 
 
 private val refreshView by bindView<SwipeRefreshLayout>(R.id.swipe) 
 private val adapter = ItemListViewAdapter(context) 
 } 
 

  85. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 subscriptions.add( 
 swipeRefreshLayout.refreshes() 
 .subscribe(refreshObserver) 
 ) 
 
 model.observeResponse() 
 .observeOn(AndroidSchedulers.mainThread()) 
 .subscribe(responseObserver) 
 
 } 
 }
  86. RxBinding

  87. final class SwipeRefreshLayoutRefreshObservable extends Observable<Object> { 
 private final SwipeRefreshLayout

    view; 
 
 SwipeRefreshLayoutRefreshObservable(SwipeRefreshLayout view) { 
 this.view = view; 
 } 
 
 @Override protected void subscribeActual( Observer<? super Object> observer) { 
 if (!checkMainThread(observer)) { 
 return; 
 } 
 Listener listener = new Listener(view, observer); 
 observer.onSubscribe(listener); 
 view.setOnRefreshListener(listener); 
 }
  88. final class SwipeRefreshLayoutRefreshObservable extends Observable<Object> { 
 private final SwipeRefreshLayout

    view; 
 
 SwipeRefreshLayoutRefreshObservable(SwipeRefreshLayout view) { 
 this.view = view; 
 } 
 
 @Override protected void subscribeActual( Observer<? super Object> observer) { 
 if (!checkMainThread(observer)) { 
 return; 
 } 
 Listener listener = new Listener(view, observer); 
 observer.onSubscribe(listener); 
 view.setOnRefreshListener(listener); 
 }
  89. final class SwipeRefreshLayoutRefreshObservable extends Observable<Object> { 
 private final SwipeRefreshLayout

    view; 
 
 SwipeRefreshLayoutRefreshObservable(SwipeRefreshLayout view) { 
 this.view = view; 
 } 
 
 @Override protected void subscribeActual( Observer<? super Object> observer) { 
 if (!checkMainThread(observer)) { 
 return; 
 } 
 Listener listener = new Listener(view, observer); 
 observer.onSubscribe(listener); 
 view.setOnRefreshListener(listener); 
 }
  90. final class SwipeRefreshLayoutRefreshObservable extends Observable<Object> { 
 private final SwipeRefreshLayout

    view; 
 
 SwipeRefreshLayoutRefreshObservable(SwipeRefreshLayout view) { 
 this.view = view; 
 } 
 
 @Override protected void subscribeActual( Observer<? super Object> observer) { 
 if (!checkMainThread(observer)) { 
 return; 
 } 
 Listener listener = new Listener(view, observer);
 observer.onSubscribe(listener); 
 view.setOnRefreshListener(listener); 
 }
  91. static final class Listener extends MainThreadDisposable implements OnRefreshListener { 


    private final SwipeRefreshLayout view; 
 private final Observer<? super Object> observer; 
 
 @Override public void onRefresh() { 
 if (!isDisposed()) { 
 observer.onNext(Notification.INSTANCE); 
 } 
 } 
 
 @Override protected void onDispose() { 
 view.setOnRefreshListener(null); 
 } 
 } 

  92. static final class Listener extends MainThreadDisposable implements OnRefreshListener { 


    private final SwipeRefreshLayout view; 
 private final Observer<? super Object> observer; 
 
 @Override public void onRefresh() { 
 if (!isDisposed()) { 
 observer.onNext(Notification.INSTANCE); 
 } 
 } 
 
 @Override protected void onDispose() { 
 view.setOnRefreshListener(null); 
 } 
 } 

  93. static final class Listener extends MainThreadDisposable implements OnRefreshListener { 


    private final SwipeRefreshLayout view; 
 private final Observer<? super Object> observer; 
 
 @Override public void onRefresh() { 
 if (!isDisposed()) { 
 observer.onNext(Notification.INSTANCE); 
 } 
 } 
 
 @Override protected void onDispose() { 
 view.setOnRefreshListener(null); 
 } 
 } 

  94. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 subscriptions.add( 
 swipeRefreshLayout.refreshes() 
 .subscribe(refreshObserver) 
 ) 
 
 subscriptions.add( 
 model.observeResponse() 
 .observeOn(AndroidSchedulers.mainThread()) 
 .subscribe(responseObserver) 
 ) 
 } 
 } 
 

  95. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 subscriptions.add( 
 swipeRefreshLayout.refreshes() 
 .subscribe(refreshObserver) 
 ) 
 
 subscriptions.add( 
 model.observeResponse() 
 .observeOn(AndroidSchedulers.mainThread()) 
 .subscribe(responseObserver) 
 ) 
 } 
 } 
 

  96. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 subscriptions.add( 
 swipeRefreshLayout.refreshes() 
 .subscribe(refreshObserver) 
 ) 
 
 subscriptions.add( 
 model.observeResponse() 
 .observeOn(AndroidSchedulers.mainThread()) 
 .subscribe(responseObserver) 
 ) 
 } private val refreshObserver = { refresh: Unit -> 
 model.refreshMessages() 
 }
 } 
 

  97. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 subscriptions.add( 
 swipeRefreshLayout.refreshes() 
 .subscribe(refreshObserver) 
 ) 
 
 subscriptions.add( 
 model.observeResponse() 
 .observeOn(AndroidSchedulers.mainThread()) 
 .subscribe(responseObserver) 
 ) 
 } 
 } 
 

  98. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 subscriptions.add( 
 swipeRefreshLayout.refreshes() 
 .subscribe(refreshObserver) 
 ) 
 
 subscriptions.add( 
 model.observeResponse() 
 .observeOn(AndroidSchedulers.mainThread()) 
 .subscribe(responseObserver) 
 ) 
 } 
 } 
 

  99. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() /* Subscriptions defined here.*/
 } private val responseObserver = { response: Response<ItemListResponse> -> 
 if (response.isSuccess()) { 
 adapter.setItems(response.items) 
 } 
 else { val error = /* Pull Throwable from response */
 log.e(error, "Error in ItemListView") 
 } 
 //refreshView.setRefreshing(false)
 refreshView.refreshing = false
 }
 }
  100. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() /* Subscriptions defined here.*/
 } private val responseObserver = { response: Response<ItemListResponse> -> 
 if (response.isSuccess()) { 
 adapter.setItems(response.items) 
 } 
 else { 
 val error = /* Pull Throwable from response */
 log.e(error, "Error in ItemListView")
 } 
 //refreshView.setRefreshing(false)
 refreshView.refreshing = false
 }
 }
  101. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() /* Subscriptions defined here.*/
 } private val responseObserver = { response: Response<ItemListResponse> -> 
 if (response.isSuccess()) { 
 adapter.setItems(response.items) 
 } 
 else { val error = /* Pull Throwable from response */
 log.e(error, "Error in ItemListView") 
 } 
 //refreshView.setRefreshing(false)
 refreshView.refreshing = false
 }
 }
  102. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() /* Subscriptions defined here.*/
 } private val responseObserver = { response: Response<ItemListResponse> -> 
 if (response.isSuccess()) { 
 adapter.setItems(response.items) 
 } 
 else { 
 val error = /* Pull Throwable from response */
 log.e(error, "Error in ItemListView")
 }
 //refreshView.setRefreshing(false)
 refreshView.refreshing = false
 }
 }
  103. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 val responseObservable = model.observeResponse() 
 .observeOn(AndroidSchedulers.mainThread()) 
 .publish() 
 
 subscriptions.add( 
 responseObservable 
 .filter { it.isSuccessful() } 
 .subscribe(successObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .filter { !it.isSuccessful() } 
 .subscribe(errorObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(refreshView.refreshing()) // RxBinding! 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .connect() 
 ) 
 }
 } 
 

  104. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 val responseObservable = model.observeResponse() 
 .observeOn(AndroidSchedulers.mainThread()) 
 .publish() 
 
 /* subscriptions created here */ subscriptions.add( 
 responseObservable 
 .connect() 
 )
 } } 
 

  105. override fun onAttachedToWindow() { super.onAttachedToWindow() 
 val responseObservable = model.observeResponse()

    
 .observeOn(AndroidSchedulers.mainThread()) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(successObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(errorObserver) 
 ) 
 }
  106. override fun onAttachedToWindow() { super.onAttachedToWindow() 
 val responseObservable = model.observeResponse()

    
 .observeOn(AndroidSchedulers.mainThread()) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(successObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(errorObserver) 
 ) subscriptions.add( 
 responseObservable.connect() 
 )
 }
  107. None
  108. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 val responseObservable = // Connectable Observable
 
 subscriptions.add( 
 responseObservable 
 .filter { it.isSuccessful() } 
 .subscribe(successObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .filter { !it.isSuccessful() } 
 .subscribe(errorObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(refreshView.refreshing()) // RxBinding! 
 ) 
 
 // Connect Observable here
 }
 } 
 

  109. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 val responseObservable = // Connectable Observable
 
 subscriptions.add( 
 responseObservable 
 .filter { it.isSuccessful() } 
 .subscribe(successObserver) 
 ) 
 } private val successObserver = { 
 adapter.setItems(response.body) 
 } }
  110. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 val responseObservable = // Connectable Observable
 
 subscriptions.add( 
 responseObservable 
 .filter { !it.isSuccessful() } 
 .subscribe(errorObserver) 
 )
 // Connect Observable here
 } private val errorObserver = { 
 val error = /* extract error from response */ 
 log.e(error, "Error in ItemListView") 
 }
 } 
 

  111. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 val responseObservable = // Connectable Observable
 
 subscriptions.add( 
 responseObservable 
 .filter { it.isSuccessful() } 
 .subscribe(successObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .filter { !it.isSuccessful() } 
 .subscribe(errorObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(refreshView.refreshing()) 
 ) 
 
 // Connect Observable here
 }
 } 
 

  112. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 val responseObservable = // Connectable Observable
 
 subscriptions.add( 
 responseObservable 
 .filter { it.isSuccessful() } 
 .subscribe(successObserver, errorObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .filter { !it.isSuccessful() } 
 .subscribe(errorObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(refreshView.refreshing()) 
 ) 
 
 // Connect Observable here
 }
 } 
 

  113. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 val responseObservable = // Connectable Observable
 
 subscriptions.add( 
 responseObservable 
 .filter { it.isSuccessful() } 
 .subscribe(successObserver, errorObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .filter { !it.isSuccessful() } 
 .subscribe(errorObserver, errorObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(refreshView.refreshing()) 
 ) 
 
 // Connect Observable here
 }
 } 
 

  114. class ItemListView : FrameLayout { 
 
 override fun onAttachedToWindow()

    { 
 super.onAttachedToWindow() 
 val responseObservable = // Connectable Observable
 
 subscriptions.add( 
 responseObservable 
 .filter { it.isSuccessful() } 
 .subscribe(successObserver, errorObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .filter { !it.isSuccessful() } 
 .subscribe(errorObserver, errorObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(refreshView.refreshing(), errorObserver) 
 ) 
 
 // Connect Observable here
 }
 } 
 

  115. RxRequests

  116. interface ItemService { 
 @GET("/items") 
 fun getItems(): Response<List<Item>> 


    } 
 

  117. @Inject 
 class GetItems(val service: ItemService) : UseCase<Response, Request> {

    
 
 override fun execute(request: Request): Observable<Response>
 
 class Request(val save: Boolean) 
 
 class Response(items: Either<Throwable, List<Item>>) 
 }
  118. @Inject 
 class GetItems(val service: ItemService) : UseCase<Response, Request> {

    
 
 override fun execute(request: Request): Observable<Response> 
 class Request(val save: Boolean) 
 
 class Response(items: Either<Throwable, List<Item>>) 
 }
  119. @Inject 
 class GetItems(val service: ItemService) : UseCase<Response, Request> {

    
 
 override fun execute(request: Request): Observable<Response> 
 class Request(val save: Boolean) 
 
 class Response(items: Either<Throwable, List<Item>>) 
 }
  120. @Inject 
 class GetItems(val service: ItemService) : UseCase<Response, Request> {

    
 
 override fun execute(request: Request): Observable<Response> 
 class Request(val save: Boolean) 
 
 class Response(items: Either<Throwable, List<Item>>) 
 }
  121. @Inject 
 class GetItems(val service: ItemService) : UseCase<Response, Request> {

    
 
 override fun execute(request: Request): Observable<Response> { 
 return service.getItems() 
 .map { response -> 
 if (response.isSuccessful) { 
 Response(Either.ofRight(response.body)) 
 } 
 else { 
 val error = /* Pull error out of Response */ 
 Response(Either.ofLeft(error)) 
 } 
 } 
 .onErrorReturn { error -> 
 Response(Either.ofLeft(error)) 
 } 
 } 
 }
  122. @Inject 
 class GetItems(val service: ItemService) : UseCase<Response, Request> {

    
 
 override fun execute(request: Request): Observable<Response> { 
 return service.getItems() 
 .map { response -> 
 if (response.isSuccessful) { 
 Response(Either.ofRight(response.body)) 
 } 
 else { 
 val error = /* Pull error out of Response */ 
 Response(Either.ofLeft(error)) 
 } 
 } 
 .onErrorReturn { error -> 
 Response(Either.ofLeft(error)) 
 } 
 } 
 }
  123. @Inject 
 class GetItems(val service: ItemService) : UseCase<Response, Request> {

    
 
 override fun execute(request: Request): Observable<Response> { 
 return service.getItems() 
 .map { response -> 
 if (response.isSuccessful) { 
 Response(Either.ofRight(response.body)) 
 } 
 else { 
 val error = /* Pull error out of Response */ 
 Response(Either.ofLeft(error)) 
 } 
 } 
 .onErrorReturn { error -> 
 Response(Either.ofLeft(error)) 
 } 
 } 
 }
  124. @Inject 
 class GetItems(val service: ItemService) : UseCase<Response, Request> {

    
 
 override fun execute(request: Request): Observable<Response> { 
 return service.getItems() 
 .map { response -> 
 if (response.isSuccessful) { 
 Response(Either.ofRight(response.body)) 
 } 
 else { 
 val error = /* Pull error out of Response */ 
 Response(Either.ofLeft(error)) 
 } 
 } 
 .onErrorReturn { error -> 
 Response(Either.ofLeft(error)) 
 } 
 } 
 }
  125. @Inject 
 class GetItems(val service: ItemService) : UseCase<Response, Request> {

    
 
 override fun execute(request: Request): Observable<Response> { 
 return service.getItems() 
 .map { response -> 
 if (response.isSuccessful) { 
 Response(Either.ofRight(response.body)) 
 } 
 else { 
 val error = /* Pull error out of Response */ 
 Response(Either.ofLeft(error)) 
 } 
 } 
 .onErrorReturn { error -> 
 Response(Either.ofLeft(error)) 
 } 
 } 
 }
  126. @Inject 
 class GetItems( val service: ItemService val db: SqliteDb)

    : UseCase<Response, Request> { 
 
 override fun execute(request: Request): Observable<Response> { 
 return service.getItems() 
 .map { response -> 
 /* Return Left or Right! */
 } 
 .onErrorReturn { error -> 
 Response(Either.ofLeft(error)) 
 } .doOnNext { either -> when (either) { is Either.Left -> { db.saveAll(either.data).subscribe() } } }
 } 
 }
  127. class ItemListViewModel(val getItems: GetItems) { 
 
 fun observeResponse(): Observable<Either<List<Item>>

    { 
 return getItems.execute(GetItems.Request(true)) 
 } 
 
 } 
 

  128. RxDatabase

  129. Observable.create() (or Flowable.create() or unsafeCreate())

  130. Observable.create() (or Flowable.create() or unsafeCreate())

  131. class DbItemDataSource(private val db: SqliteDb) : DbDataSource<Item> { 
 


    override fun saveAll(items: List<Item>): Observable<Boolean> { 
 Observable.fromCallable { 
 items.forEach { 
 db.replaceOrThrow("items", null, items.toQueryString()) 
 } != -1L 
 } 
 } 
 }
  132. RxTesting

  133. 
 @RunWith(RobolectricTestRunner::class.java) 
 class ItemListViewTest { 
 
 private var

    model: ItemListViewModel = /* Instantiate */
 private val myView: ItemListView = /* Create View */ 
 
 private val goodResponse: = 
 Either.Right(/* List of Items */) 
 
 @Test 
 fun testAdapterItems() { 
 Mockito.when(model.fetchItems()) .thenReturn(goodResponse) 
 myView.invokeRefresh() 
 assertEquals( goodResponse.data, myView.getAdapter().items) 
 } 
 }
  134. 
 @RunWith(RobolectricTestRunner::class.java) 
 class ItemListViewTest { 
 
 private var

    model: ItemListViewModel = /* Instantiate */
 private val myView: ItemListView = /* Create View */ 
 
 private val goodResponse: = 
 Either.Right(/* List of Items */) 
 
 @Test 
 fun testAdapterItems() { 
 Mockito.when(model.fetchItems()) .thenReturn(goodResponse) 
 myView.invokeRefresh() 
 assertEquals( goodResponse.data, myView.getAdapter().items) 
 } 
 }
  135. 
 @RunWith(RobolectricTestRunner::class.java) 
 class ItemListViewTest { 
 
 private var

    model: ItemListViewModel = /* Instantiate */
 private val myView: ItemListView = /* Create View */ 
 
 private val goodResponse: = 
 Either.Right(/* List of Items */) 
 
 @Test 
 fun testAdapterItems() { 
 Mockito.when(model.fetchItems()) .thenReturn(goodResponse) 
 myView.invokeRefresh() 
 assertEquals( goodResponse.data, myView.getAdapter().items) 
 } 
 }
  136. 
 @RunWith(RobolectricTestRunner::class.java) 
 class ItemListViewTest { 
 
 private var

    model: ItemListViewModel = /* Instantiate */
 private val myView: ItemListView = /* Create View */ 
 
 private val goodResponse: = 
 Either.Right(/* List of Items */) 
 
 @Test 
 fun testAdapterItems() { 
 Mockito.when(model.fetchItems()) .thenReturn(goodResponse) 
 myView.invokeRefresh() 
 assertEquals( goodResponse.data, myView.getAdapter().items) 
 } 
 }
  137. 
 @RunWith(RobolectricTestRunner::class.java) 
 class ItemListViewTest { 
 
 private var

    model: ItemListViewModel = /* Instantiate */
 private val myView: ItemListView = /* Create View */ 
 
 private val goodResponse: = 
 Either.Right(/* List of Items */) 
 
 @Test 
 fun testAdapterItems() { 
 Mockito.when(model.fetchItems()) .thenReturn(goodResponse) 
 myView.invokeRefresh() 
 assertEquals( goodResponse.data, myView.getAdapter().items) 
 } 
 }
  138. 
 @RunWith(RobolectricTestRunner::class.java) 
 class ItemListViewTest { 
 
 private var

    model: ItemListViewModel = /* Instantiate */
 private val myView: ItemListView = /* Create View */ 
 
 private val goodResponse: = 
 Either.Right(/* List of Items */) 
 
 @Test 
 fun testAdapterItems() { 
 Mockito.when(model.fetchItems()) .thenReturn(goodResponse) 
 myView.invokeRefresh() 
 assertEquals( goodResponse.data, myView.getAdapter().items) 
 } 
 }
  139. 
 @RunWith(RobolectricTestRunner::class.java) 
 class ItemListViewTest { 
 
 private var

    model: ItemListViewModel = /* Instantiate */
 private val myView: ItemListView = /* Create View */ 
 
 private val goodResponse: = Either.Right(/* */) @Before 
 fun setUp() { 
 RxJavaHooks.reset() 
 RxJavaHooks.setOnIOScheduler { Schedulers.immediate() } 
 
 RxJavaHooks.setOnNewThreadScheduler { Schedulers.immediate() } 
 } 
 
 @Test 
 fun testAdapterItems() { 
 /* Test code */
 } 
 }
  140. Architectures? • Redux • MVVM • Cycle.js clone (kevcar.me/mvwtf)

  141. Questions? kevcar.me/rxapplications