$30 off During Our Annual Pro Sale. View Details »

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. RxApplications
    Kevin Carpenter
    Notion AI

    View Slide

  2. Application behavior is asynchronous.

    View Slide

  3. Pull to Refresh

    View Slide

  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”
    }]

    View Slide

  5. INSERT INTO items (title, author, points, date)

    VALUES (‘Rx’, ‘kevcar’, 90, 1492278237562)

    View Slide

  6. DELETE FROM items WHERE date <
    1492278000 AND date > 1492280000

    View Slide

  7. View Slide

  8. View Slide



  9. View Slide



  10. swipeRefreshLayout.setOnRefreshListener(
    new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh() {
    // make update request
    }
    }
    );

    View Slide

  11. RxJava

    View Slide

  12. RxJava
    RxJS

    View Slide

  13. RxJava
    RxJS
    RxKotlin

    View Slide

  14. RxJava
    RxJS
    RxKotlin
    RxSwift

    View Slide

  15. “ReactiveX is a combination of the best ideas from the
    Observer pattern, the Iterator pattern, and functional programming”

    View Slide

  16. abstract class Observer {

    fun update(T notification)

    }

    View Slide

  17. abstract class Observable {

    private val ArrayList> observers; 


    fun registerObserver(Observer observer) { 

    observers.add(observer); 

    } 


    fun unregisterObserver(Observer observer) { 

    observers.remove(observer); 

    } 

    } 


    View Slide

  18. abstract class Observable {

    private val observers: ArrayList> 


    fun registerObserver(observer: Observer) { 

    observers.add(observer); 

    } 


    fun unregisterObserver(observer: Observer) { 

    observers.remove(observer); 

    } 

    } 


    View Slide

  19. abstract class Observable { 

    private val observers: ArrayList>


    void registerObserver(observer: Observer) { 

    observers.add(observer) 

    } 


    void unregisterObserver(observer: Observer) { 

    observers.remove(observer);

    } 


    void setValue(newValue: T) { 

    this.value = newValue

    notifyObservers(newValue)

    } 

    } 


    View Slide

  20. abstract class Observable { 

    private val observers: ArrayList> ; 


    fun registerObserver(observer: Observer) { 

    observers.add(observer); 

    } 


    fun unregisterObserver(observer: Observer) { 

    observers.remove(observer); 

    } 


    fun setValue(newValue: T) { 

    this.value = newValue; 

    notifyObservers(newValue); 

    } 


    fun notifyObservers(value: T) { 

    for (observer : observers) { 

    observer.update(value); 

    } 

    } 

    }

    View Slide

  21. “ReactiveX is a combination of the best ideas from the
    Observer pattern, the Iterator pattern, and functional programming”

    View Slide

  22. interface Iterable 


    fun iterator(): Iterator 


    }

    View Slide

  23. interface Iterator 


    fun hasNext(): Boolean 


    fun next(): T 


    }

    View Slide

  24. val sounds: Iterable = listOf(
    "moo", "cluck", “quack"
    ) 


    while (sounds.iterator().hasNext()) { 

    log.d("Animal Sound", sounds.next()) 

    }

    View Slide

  25. val sounds: Iterable = listOf(
    "moo", "cluck", “quack"
    ) 


    while (sounds.iterator().hasNext()) { 

    log.d("Animal Sound", sounds.next()) 

    }
    D/Animal Sound: moo

    View Slide

  26. val sounds: Iterable = listOf(
    "moo", "cluck", “quack"
    ) 


    while (sounds.iterator().hasNext()) { 

    log.d("Animal Sound", sounds.next()) 

    }
    D/Animal Sound: moo
    D/Animal Sound: cluck

    View Slide

  27. val sounds: Iterable = 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


    View Slide

  28. val sounds: Iterable = listOf(
    "moo", "cluck", “quack"
    ) 


    sounds.map { sound -> 

    sound + sound // "$sound $sound" 

    } 

    .map { doubleSound -> 

    log.d("Animal Sound", doubleSound) 

    }

    View Slide

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


    sounds.map { sound -> 

    sound + sound // "$sound $sound" 

    } 

    .map { doubleSound -> 

    log.d("Animal Sound", doubleSound) 

    }
    D/Animal Sound: moo moo


    View Slide

  30. val sounds: Iterable = 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

    View Slide

  31. val sounds: Iterable = 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


    View Slide

  32. “ReactiveX is a combination of the best ideas from the
    Observer pattern, the Iterator pattern, and functional programming”

    View Slide

  33. f(x) = y

    View Slide

  34. { x ->
    x * x;
    }

    View Slide

  35. int square(int x) { 

    int square = x * x; 

    textView.text = square; 

    return result; 

    } 


    View Slide

  36. int square(int x) { 

    int square = x * x; 

    this.state.latestSquare = square; 

    return result; 

    } 


    View Slide

  37. // 1, 2, 3

    Observable intObservable = Observable.range(1, 4) 


    intObservable.map { x -> 

    x * x 

    } 

    .map { squared -> 

    "Result: " + squared 

    } 

    .map { str -> 

    log.d(str) 

    }

    View Slide

  38. Observable

    View Slide

  39. time

    View Slide

  40. time

    View Slide

  41. time

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  45. Observable.from(listOf("moo", "cluck", "quack"))
    time
    moo cluck quack

    View Slide

  46. time
    moo cluck
    X

    View Slide

  47. time
    moo X

    View Slide

  48. time
    moo cluck

    View Slide

  49. Observable.subscribe(
    Subscriber subscriber
    )

    View Slide

  50. abstract class Subscriber {

    onNext(T t);

    onError(Throwable e); // Terminal

    onCompleted(); // Terminal 

    } 


    View Slide

  51. abstract class Subscriber { 

    onNext(T t); 

    onError(Throwable e); // Terminal 

    onCompleted(); // Terminal 

    } 


    Observable.subscribe( 

    Action1 onNext, 

    Action1, onError, 

    Action0 onCompleted) 


    View Slide

  52. val action = Action1 { 

    fun event (event: String) { 

    log.d("Animal Sound", event) 

    } 

    } 


    val errorAction = Action1 { 

    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)

    View Slide

  53. val action = Action1 { 

    fun event (event: String) { 

    log.d("Animal Sound", event) 

    } 

    } 


    val errorAction = Action1 { 

    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)

    View Slide

  54. val action = Action1 { 

    fun event (event: String) { 

    log.d("Animal Sound", event) 

    } 

    } 


    val errorAction = Action1 { 

    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)

    View Slide

  55. val action = Action1 { 

    fun event (event: String) { 

    log.d("Animal Sound", event) 

    } 

    } 


    val errorAction = Action1 { 

    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)

    View Slide

  56. Observable.from(listOf("moo", "cluck", “quack"))
    time

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  60. Observable.from(listOf("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

    View Slide

  61. Observable.from(listOf("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

    View Slide

  62. Non-RxViews

    View Slide

  63. Pull to Refresh

    View Slide

  64. class ItemListView : FrameLayout, IItemListView { 


    @Inject 

    private val model: ItemListViewModel 


    private val refreshView by bindView(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() 

    } 

    }
    }

    View Slide

  65. class ItemListView : FrameLayout, IItemListView { 


    @Inject 

    private val model: ItemListViewModel 


    private val refreshView by bindView(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() 

    } 

    }
    }

    View Slide

  66. class ItemListView : FrameLayout, IItemListView { 


    @Inject 

    private val model: ItemListViewModel 


    private val refreshView by bindView(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() 

    } 

    }
    }

    View Slide

  67. class ItemListView : FrameLayout, IItemListView { 


    @Inject 

    private val model: ItemListViewModel 


    private val refreshView by bindView(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() 

    } 

    }
    }

    View Slide

  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() 

    }
    }

    View Slide

  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() 

    }
    }

    View Slide

  70. 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() 

    }
    }

    View Slide

  71. interface IItemListView { 


    fun onItemsResponse(items: List) 


    fun onItemsError(error: Throwable) 


    fun onRefreshComplete() 

    } 


    View Slide

  72. interface IItemListView { 


    fun onItemsResponse(items: List) 


    fun onItemsError(error: Throwable) 


    fun onRefreshComplete() 

    } 


    View Slide

  73. interface IItemListView { 


    fun onItemsResponse(items: List) 


    fun onItemsError(error: Throwable) 


    fun onRefreshComplete() 

    } 


    View Slide

  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() 

    } 

    }
    }

    View Slide

  75. 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() 

    } 

    }
    }

    View Slide

  76. class ItemListView : FrameLayout, IItemListView { 


    private val listener = object : OnRefreshListener { 

    override fun onRefresh() { 

    model.requestRefresh() 

    } 

    } 


    override fun onItemsResponse(items: List) { 

    adapter.setItems(items) 

    } 


    override fun onItemsError(error: Throwable) { 

    log.e(error, "Error in ItemListView") 

    } 


    override fun onRefreshComplete() { 

    refreshView.setRefreshing(false) 

    } 


    }

    View Slide

  77. class ItemListView : FrameLayout, IItemListView { 


    private val listener = object : OnRefreshListener { 

    override fun onRefresh() { 

    model.requestRefresh() 

    } 

    } 


    override fun onItemsResponse(items: List) { 

    adapter.setItems(items) 

    } 


    override fun onItemsError(error: Throwable) { 

    log.e(error, "Error in ItemListView") 

    } 


    override fun onRefreshComplete() { 

    refreshView.setRefreshing(false) 

    } 


    }

    View Slide

  78. class ItemListView : FrameLayout, IItemListView { 


    private val listener = object : OnRefreshListener { 

    override fun onRefresh() { 

    model.requestRefresh() 

    } 

    } 


    override fun onItemsResponse(items: List) { 

    adapter.setItems(items) 

    } 


    override fun onItemsError(error: Throwable) { 

    log.e(error, "Error in ItemListView") 

    } 


    override fun onRefreshComplete() { 

    refreshView.setRefreshing(false) 

    } 


    }

    View Slide

  79. class ItemListView : FrameLayout, IItemListView { 


    private val listener = object : OnRefreshListener { 

    override fun onRefresh() { 

    model.requestRefresh() 

    } 

    } 


    override fun onItemsResponse(items: List) { 

    adapter.setItems(items) 

    } 


    override fun onItemsError(error: Throwable) { 

    log.e(error, "Error in ItemListView") 

    } 


    override fun onRefreshComplete() { 

    refreshView.setRefreshing(false) 

    } 


    }

    View Slide

  80. interface IItemListView { 


    fun onItemsResponse(items: List)
    fun onItemsError(error: Throwable)


    fun onRefreshComplete()

    } 


    View Slide

  81. interface IItemListView { 


    fun onItemsResponse(items: List)
    fun onItemsError(error: Throwable)


    fun onRefreshComplete()
    fun onNextResponsePage(items: List)
    fun onNextResponsePageError(error: Throwable)


    fun onNextResponsePageComplete()

    } 


    View Slide

  82. RxViews

    View Slide

  83. Pull to Refresh

    View Slide

  84. class ItemListView : FrameLayout { 


    @Inject 

    private val model: ItemListViewModel 

    private val subscriptions = CompositeSubscription() 


    private val refreshView by bindView(R.id.swipe) 

    private val adapter = ItemListViewAdapter(context)

    } 


    View Slide

  85. class ItemListView : FrameLayout { 


    @Inject 

    private val model: ItemListViewModel 

    private val subscriptions = CompositeSubscription() 


    private val refreshView by bindView(R.id.swipe) 

    private val adapter = ItemListViewAdapter(context)

    } 


    View Slide

  86. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()

    subscriptions.add( 

    swipeRefreshLayout.refreshes() 

    .subscribe(refreshObserver) 

    ) 


    model.observeResponse() 

    .observeOn(AndroidSchedulers.mainThread()) 

    .subscribe(responseObserver) 


    } 

    }

    View Slide

  87. RxBinding

    View Slide

  88. final class SwipeRefreshLayoutRefreshObservable
    extends Observable { 

    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); 

    }

    View Slide

  89. final class SwipeRefreshLayoutRefreshObservable
    extends Observable { 

    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); 

    }

    View Slide

  90. final class SwipeRefreshLayoutRefreshObservable
    extends Observable { 

    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); 

    }

    View Slide

  91. final class SwipeRefreshLayoutRefreshObservable
    extends Observable { 

    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); 

    }

    View Slide

  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); 

    } 

    } 


    View Slide

  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); 

    } 

    } 


    View Slide

  94. 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); 

    } 

    } 


    View Slide

  95. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()

    subscriptions.add( 

    swipeRefreshLayout.refreshes() 

    .subscribe(refreshObserver) 

    ) 


    subscriptions.add( 

    model.observeResponse() 

    .observeOn(AndroidSchedulers.mainThread()) 

    .subscribe(responseObserver) 

    ) 

    } 

    } 


    View Slide

  96. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()

    subscriptions.add( 

    swipeRefreshLayout.refreshes() 

    .subscribe(refreshObserver) 

    ) 


    subscriptions.add( 

    model.observeResponse() 

    .observeOn(AndroidSchedulers.mainThread()) 

    .subscribe(responseObserver) 

    ) 

    }

    } 


    View Slide

  97. 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() 

    }

    } 


    View Slide

  98. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()

    subscriptions.add( 

    swipeRefreshLayout.refreshes() 

    .subscribe(refreshObserver) 

    ) 


    subscriptions.add( 

    model.observeResponse() 

    .observeOn(AndroidSchedulers.mainThread()) 

    .subscribe(responseObserver) 

    ) 

    } 

    } 


    View Slide

  99. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()

    subscriptions.add( 

    swipeRefreshLayout.refreshes() 

    .subscribe(refreshObserver) 

    ) 


    subscriptions.add( 

    model.observeResponse() 

    .observeOn(AndroidSchedulers.mainThread()) 

    .subscribe(responseObserver) 

    ) 

    } 

    } 


    View Slide

  100. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()
    /* Subscriptions defined here.*/

    }
    private val responseObserver = {
    response: Response -> 

    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

    }

    }

    View Slide

  101. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()
    /* Subscriptions defined here.*/

    }
    private val responseObserver = {
    response: Response -> 

    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

    }

    }

    View Slide

  102. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()
    /* Subscriptions defined here.*/

    }
    private val responseObserver = {
    response: Response -> 

    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

    }

    }

    View Slide

  103. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()
    /* Subscriptions defined here.*/

    }
    private val responseObserver = {
    response: Response -> 

    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

    }

    }

    View Slide

  104. 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() 

    ) 

    }

    } 


    View Slide

  105. class ItemListView : FrameLayout { 


    override fun onAttachedToWindow() { 

    super.onAttachedToWindow()

    val responseObservable = model.observeResponse() 

    .observeOn(AndroidSchedulers.mainThread()) 

    .publish() 


    /* subscriptions created here */
    subscriptions.add( 

    responseObservable 

    .connect() 

    )

    }
    } 


    View Slide

  106. override fun onAttachedToWindow() {
    super.onAttachedToWindow()

    val responseObservable = model.observeResponse() 

    .observeOn(AndroidSchedulers.mainThread()) 


    subscriptions.add( 

    responseObservable 

    .subscribe(successObserver) 

    ) 


    subscriptions.add( 

    responseObservable 

    .subscribe(errorObserver) 

    ) 

    }

    View Slide

  107. 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() 

    )

    }

    View Slide

  108. View Slide

  109. 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

    }

    } 


    View Slide

  110. 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) 

    }
    }

    View Slide

  111. 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") 

    }

    } 


    View Slide

  112. 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

    }

    } 


    View Slide

  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) 

    ) 


    subscriptions.add( 

    responseObservable 

    .subscribe(refreshView.refreshing()) 

    ) 


    // Connect Observable here

    }

    } 


    View Slide

  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()) 

    ) 


    // Connect Observable here

    }

    } 


    View Slide

  115. 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

    }

    } 


    View Slide

  116. RxRequests

    View Slide

  117. interface ItemService {

    @GET("/items") 

    fun getItems(): Response>

    } 


    View Slide

  118. @Inject 

    class GetItems(val service: ItemService) : UseCase { 


    override fun execute(request: Request): Observable


    class Request(val save: Boolean) 


    class Response(items: Either>) 

    }

    View Slide

  119. @Inject 

    class GetItems(val service: ItemService) : UseCase { 


    override fun execute(request: Request): Observable

    class Request(val save: Boolean) 


    class Response(items: Either>) 

    }

    View Slide

  120. @Inject 

    class GetItems(val service: ItemService) : UseCase { 


    override fun execute(request: Request): Observable

    class Request(val save: Boolean) 


    class Response(items: Either>) 

    }

    View Slide

  121. @Inject 

    class GetItems(val service: ItemService) : UseCase { 


    override fun execute(request: Request): Observable

    class Request(val save: Boolean) 


    class Response(items: Either>) 

    }

    View Slide

  122. @Inject 

    class GetItems(val service: ItemService) : UseCase { 


    override fun execute(request: Request): Observable { 

    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)) 

    } 

    } 

    }

    View Slide

  123. @Inject 

    class GetItems(val service: ItemService) : UseCase { 


    override fun execute(request: Request): Observable { 

    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)) 

    } 

    } 

    }

    View Slide

  124. @Inject 

    class GetItems(val service: ItemService) : UseCase { 


    override fun execute(request: Request): Observable { 

    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)) 

    } 

    } 

    }

    View Slide

  125. @Inject 

    class GetItems(val service: ItemService) : UseCase { 


    override fun execute(request: Request): Observable { 

    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)) 

    } 

    } 

    }

    View Slide

  126. @Inject 

    class GetItems(val service: ItemService) : UseCase { 


    override fun execute(request: Request): Observable { 

    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)) 

    } 

    } 

    }

    View Slide

  127. @Inject 

    class GetItems(
    val service: ItemService
    val db: SqliteDb) : UseCase { 


    override fun execute(request: Request): Observable { 

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

    } 

    }

    View Slide

  128. class ItemListViewModel(val getItems: GetItems) { 


    fun observeResponse(): Observable> { 

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

    } 


    } 


    View Slide

  129. RxDatabase

    View Slide

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

    View Slide

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

    View Slide

  132. class DbItemDataSource(private val db: SqliteDb) : DbDataSource { 


    override fun saveAll(items: List): Observable { 

    Observable.fromCallable { 

    items.forEach { 

    db.replaceOrThrow("items", null, items.toQueryString()) 

    } != -1L 

    } 

    } 

    }

    View Slide

  133. RxTesting

    View Slide


  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) 

    } 

    }

    View Slide


  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) 

    } 

    }

    View Slide


  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) 

    } 

    }

    View Slide


  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) 

    } 

    }

    View Slide


  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) 

    } 

    }

    View Slide


  139. @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) 

    } 

    }

    View Slide


  140. @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 */

    } 

    }

    View Slide



  141. View Slide



  142. View Slide



  143. View Slide



  144. View Slide

  145. Architectures?
    • Redux
    • MVVM
    • Cycle.js clone (kevcar.me/mvwtf)

    View Slide

  146. Questions?
    kevcar.me/rxapplications

    View Slide