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

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.

Kevin Carpenter

April 22, 2017
Tweet

More Decks by Kevin Carpenter

Other Decks in Programming

Transcript

  1. 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” }]
  2. “ReactiveX is a combination of the best ideas from the

    Observer pattern, the Iterator pattern, and functional programming”
  3. 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); 
 } 
 } 

  4. 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); 
 } 
 } 

  5. 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)
 } 
 } 

  6. 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); 
 } 
 } 
 }
  7. “ReactiveX is a combination of the best ideas from the

    Observer pattern, the Iterator pattern, and functional programming”
  8. val sounds: Iterable<String> = listOf( "moo", "cluck", “quack" ) 


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


    
 while (sounds.iterator().hasNext()) { 
 log.d("Animal Sound", sounds.next()) 
 } D/Animal Sound: moo
  10. 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
  11. 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

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


    
 sounds.map { sound -> 
 sound + sound // "$sound $sound" 
 } 
 .map { doubleSound -> 
 log.d("Animal Sound", doubleSound) 
 }
  13. 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

  14. 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
  15. 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

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

    Observer pattern, the Iterator pattern, and functional programming”
  17. int square(int x) { 
 int square = x *

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

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

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

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


    
 intObservable.map { x -> 
 x * x 
 } 
 .map { squared -> 
 "Result: " + squared 
 } 
 .map { str -> 
 log.d(str) 
 }
  20. abstract class Subscriber<T> { 
 onNext(T t); 
 onError(Throwable e);

    // Terminal 
 onCompleted(); // Terminal 
 } 

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

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

  22. 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)
  23. 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)
  24. 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)
  25. 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)
  26. 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
  27. 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
  28. 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() 
 } 
 } }
  29. 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() 
 } 
 } }
  30. 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() 
 } 
 } }
  31. 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() 
 } 
 } }
  32. 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() 
 } }
  33. 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() 
 } }
  34. 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() 
 } }
  35. interface IItemListView { 
 
 fun onItemsResponse(items: List<Item>) 
 


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

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


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

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


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

  38. 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() 
 } 
 } }
  39. 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() 
 } 
 } }
  40. 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) 
 } 
 
 }
  41. 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) 
 } 
 
 }
  42. 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) 
 } 
 
 }
  43. 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) 
 } 
 
 }
  44. interface IItemListView { 
 
 fun onItemsResponse(items: List<Item>) fun onItemsError(error:

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

  45. 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) 
 } 
 

  46. 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) 
 } 
 

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

    { 
 super.onAttachedToWindow() 
 subscriptions.add( 
 swipeRefreshLayout.refreshes() 
 .subscribe(refreshObserver) 
 ) 
 
 model.observeResponse() 
 .observeOn(AndroidSchedulers.mainThread()) 
 .subscribe(responseObserver) 
 
 } 
 }
  48. 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); 
 }
  49. 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); 
 }
  50. 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); 
 }
  51. 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); 
 }
  52. 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); 
 } 
 } 

  53. 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); 
 } 
 } 

  54. 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); 
 } 
 } 

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

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

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

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

  57. 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() 
 }
 } 
 

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

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

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

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

  60. 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
 }
 }
  61. 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
 }
 }
  62. 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
 }
 }
  63. 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
 }
 }
  64. 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() 
 ) 
 }
 } 
 

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

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

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

    
 .observeOn(AndroidSchedulers.mainThread()) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(successObserver) 
 ) 
 
 subscriptions.add( 
 responseObservable 
 .subscribe(errorObserver) 
 ) 
 }
  67. 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() 
 )
 }
  68. 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
 }
 } 
 

  69. 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) 
 } }
  70. 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") 
 }
 } 
 

  71. 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
 }
 } 
 

  72. 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
 }
 } 
 

  73. 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
 }
 } 
 

  74. 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
 }
 } 
 

  75. @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>>) 
 }
  76. @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>>) 
 }
  77. @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>>) 
 }
  78. @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>>) 
 }
  79. @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)) 
 } 
 } 
 }
  80. @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)) 
 } 
 } 
 }
  81. @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)) 
 } 
 } 
 }
  82. @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)) 
 } 
 } 
 }
  83. @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)) 
 } 
 } 
 }
  84. @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() } } }
 } 
 }
  85. class ItemListViewModel(val getItems: GetItems) { 
 
 fun observeResponse(): Observable<Either<List<Item>>

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

  86. 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 
 } 
 } 
 }
  87. 
 @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) 
 } 
 }
  88. 
 @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) 
 } 
 }
  89. 
 @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) 
 } 
 }
  90. 
 @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) 
 } 
 }
  91. 
 @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) 
 } 
 }
  92. 
 @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) 
 } 
 }
  93. 
 @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 */
 } 
 }