The State of Managing State with RxJava (Devoxx US 2017)

The State of Managing State with RxJava (Devoxx US 2017)

RxJava's use in building Android apps has grown rapidly! Combining and composing synchronous and asynchronous sources of data has become easy. That burden that remains is where to put the overall state of the application and how to handle its changes. This talk will be an exploration in how we can increase the confidence and determinism of our app state.

Because Rx isn't specific to Android, we'll look at state management approaches employed by other platforms and languages and whether or not they're appropriate to use. We will also look at how state management fits into application architectures such as MVP and MVVM.

Attendees of this talk should already be comfortable with reactive programming and RxJava's APIs.

Video: https://youtu.be/0IKHxjkgop4

E68309f117985270285ade8082f4877d?s=128

Jake Wharton

March 21, 2017
Tweet

Transcript

  1. Managing State with RxJava Jake Wharton

  2. Why Reactive? Unless you can model your entire system synchronously...

  3. Why Reactive? Unless you can model your entire system synchronously,

    a single asynchronous source breaks imperative programming.
  4. Why Reactive? interface UserManager { User getUser();
 }A

  5. Why Reactive? interface UserManager { User getUser(); void setName(String name);

    void setAge(int age);
 }A
  6. Why Reactive? interface UserManager { User getUser(); void setName(String name);

    void setAge(int age);
 }A UserManager um = new UserManager();
  7. Why Reactive? interface UserManager { User getUser(); void setName(String name);

    void setAge(int age);
 }A UserManager um = new UserManager(); System.out.println(um.getUser());
  8. Why Reactive? interface UserManager { User getUser(); void setName(String name);

    void setAge(int age);
 }A UserManager um = new UserManager(); System.out.println(um.getUser()); um.setName("Jane Doe");
  9. Why Reactive? interface UserManager { User getUser(); void setName(String name);

    void setAge(int age);
 }A UserManager um = new UserManager(); System.out.println(um.getUser()); um.setName("Jane Doe"); System.out.println(um.getUser());
  10. Why Reactive? interface UserManager { User getUser(); void setName(String name);

    // <-- now async void setAge(int age); // <-- now async
 }A
  11. Why Reactive? interface UserManager { User getUser(); void setName(String name);

    void setAge(int age);
 }A UserManager um = new UserManager(); System.out.println(um.getUser()); um.setName("Jane Doe"); System.out.println(um.getUser());
  12. Why Reactive? interface UserManager { User getUser(); void setName(String name,

    Runnable callback); void setAge(int age, Runnable callback);
 }A
  13. Why Reactive? interface UserManager { User getUser(); void setName(String name,

    Runnable callback);A void setAge(int age, Runnable callback);B
 }A UserManager um = new UserManager(); System.out.println(um.getUser()); um.setName("Jane Doe", () -> { System.out.println(um.getUser()); });
  14. Why Reactive? interface UserManager { User getUser(); void setName(String name,

    Listener listener);A void setAge(int age, Listener listener);B interface Listener { void success(User user); void failure(IOException e); }G
 }A
  15. Why Reactive? UserManager um = new UserManager(); System.out.println(um.getUser()); um.setName("Jane Doe");

  16. Why Reactive? UserManager um = new UserManager(); System.out.println(um.getUser()); um.setName("Jane Doe",

    new UserManager.Listener() { @Override public void success() { System.out.println(um.getUser()); }A @Override public void failure(IOException e) { // TODO show the error... }B });
  17. Why Reactive? UserManager um = new UserManager(); System.out.println(um.getUser()); um.setName("Jane Doe",

    new UserManager.Listener() { @Override public void success() { System.out.println(um.getUser()); }A @Override public void failure(IOException e) { // TODO show the error... }B }); um.setAge(40, new UserManager.Listener() { @Override public void success() { System.out.println(um.getUser()); }C 2@Override public void failure(IOException e) {2 2// TODO show the error...2 }D });2
  18. Why Reactive? UserManager um = new UserManager(); System.out.println(um.getUser()); um.setName("Jane Doe",

    new UserManager.Listener() { @Override public void success() { System.out.println(um.getUser()); um.setAge(40, new UserManager.Listener() { @Override public void success() { System.out.println(um.getUser()); }C 2@Override public void failure(IOException e) {2 2// TODO show the error...2 }D });2 }A @Override public void failure(IOException e) { // TODO show the error... }B });
  19. Why Reactive? public final class UserActivity extends Activity { private

    final UserManager um = new UserManager(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user); TextView tv = (TextView) findViewById(R.id.user_name); tv.setText(um.getUser().toString()); um.setName("Jane Doe", new UserManager.Listener() { @Override public void success() { tv.setText(um.getUser().toString()); }A @Override public void failure(IOException e) { // TODO show the error... }B }); }Y }Z
  20. Why Reactive? public final class UserActivity extends Activity { private

    final UserManager um = new UserManager(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user); TextView tv = (TextView) findViewById(R.id.user_name); tv.setText(um.getUser().toString()); um.setName("Jane Doe", new UserManager.Listener() { @Override public void success() { tv.setText(um.getUser().toString()); }A @Override public void failure(IOException e) { // TODO show the error... }B }); }Y }Z
  21. Why Reactive? public final class UserActivity extends Activity { private

    final UserManager um = new UserManager(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user); TextView tv = (TextView) findViewById(R.id.user_name); tv.setText(um.getUser().toString()); um.setName("Jane Doe", new UserManager.Listener() { @Override public void success() { if (isDestroyed()) { tv.setText(um.getUser().toString()); } }A @Override public void failure(IOException e) { // TODO show the error... }B }); }Y }Z
  22. Why Reactive? public final class UserActivity extends Activity { private

    final UserManager um = new UserManager(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user); TextView tv = (TextView) findViewById(R.id.user_name); tv.setText(um.getUser().toString()); um.setName("Jane Doe", new UserManager.Listener() { @Override public void success() { if (isDestroyed()) { tv.setText(um.getUser().toString()); } }A @Override public void failure(IOException e) { // TODO show the error... }B }); }Y }Z
  23. Why Reactive? public final class UserActivity extends Activity { private

    final UserManager um = new UserManager(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user); TextView tv = (TextView) findViewById(R.id.user_name); tv.setText(um.getUser().toString()); um.setName("Jane Doe", new UserManager.Listener() { @Override public void success() { if (isDestroyed()) { tv.setText(um.getUser().toString()); }L }A @Override public void failure(IOException e) { // TODO show the error... }B }); }Y }Z
  24. Why Reactive? public final class UserActivity extends Activity { private

    final UserManager um = new UserManager(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user); TextView tv = (TextView) findViewById(R.id.user_name); tv.setText(um.getUser().toString()); um.setName("Jane Doe", new UserManager.Listener() { @Override public void success() { runOnUiThread(new Runnable() { @Override public void run() { if (isDestroyed()) { tv.setText(um.getUser().toString()); }L }4 }); }A @Override public void failure(IOException e) { // TODO show the error... }B }); }Y }Z
  25. Why Reactive? public final class UserActivity extends Activity { private

    final UserManager um = new UserManager(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user); TextView tv = (TextView) findViewById(R.id.user_name); tv.setText(um.getUser().toString()); um.setName("Jane Doe", new UserManager.Listener() { @Override public void success() { runOnUiThread(new Runnable() { @Override public void run() { if (isDestroyed()) { tv.setText(um.getUser().toString()); } }4 }); }A @Override public void failure(IOException e) { // TODO show the error... }B }); }Y }Z
  26. Why Reactive? GET / 200 OK SELECT * Jane Doe

    setText onClick
  27. Why Reactive? GET / 200 OK SELECT * Jane Doe

    setText onClick
  28. Why Reactive? GET / 200 OK SELECT * Jane Doe

    setText onClick
  29. Why Reactive? GET / 200 OK SELECT * Jane Doe

    setText onClick
  30. Why Reactive? GET / 200 OK SELECT * Jane Doe

    setText onClick
  31. Why Reactive? GET / 200 OK SELECT * Jane Doe

    setText onClick SELECT * Jane Doe setText UPDATE user Jane Doe setText
  32. Why Reactive?

  33. Why Reactive? Unless you can model your entire system synchronously,

    a single asynchronous source breaks imperative programming.
  34. Why Reactive?

  35. Why Reactive?

  36. Why Reactive?

  37. Why Reactive?

  38. Why Reactive?

  39. Why Reactive?

  40. interface UserManager { User getUser(); void setName(String name); void setAge(int

    age);
 }A Being Reactive
  41. Being Reactive interface UserManager { Observable<User> getUser(); void setName(String name);

    void setAge(int age);
 }A
  42. Being Reactive interface UserManager { Observable<User> getUser(); Completable setName(String name);

    Completable setAge(int age);
 }A void void

  43. Being Reactive um.getUser()

  44. Being Reactive um.getUser() .observeOn(AndroidSchedulers.mainThread())

  45. Being Reactive um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override public void

    onNext(User user) { }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } });
  46. Being Reactive um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override public void

    onNext(User user) { tv.setText(user.toString()); }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } });2
  47. Being Reactive disposables.add(um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override public void

    onNext(User user) { tv.setText(user.toString()); }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } }));2
  48. Being Reactive // onCreate disposables.add(um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override

    public void onNext(User user) { tv.setText(user.toString()); }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } })); // onDestroy disposables.dispose();
  49. Being Reactive um.setName("Jane Doe")

  50. Being Reactive um.setName("Jane Doe") .subscribeOn(Schedulers.io())

  51. Being Reactive um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())

  52. Being Reactive um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { @Override

    public void onComplete() { }1 @Override public void onError(Throwable t) { // retry or show }2 });
  53. Being Reactive um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { @Override

    public void onComplete() { // success! re-enable editing }1 @Override public void onError(Throwable t) { // retry or show }2 });3
  54. Being Reactive disposables.add(um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { @Override

    public void onComplete() { // success! re-enable editing }1 @Override public void onError(Throwable t) { // retry or show }2 }));3
  55. Being Reactive // onCreate disposables.add(um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override

    public void onNext(User user) { tv.setText(user.toString()); }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } })); // onDestroy disposables.dispose(); // button click listener disposables.add(um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { @Override public void onComplete() { // success! re-enable editing }1 @Override public void onError(Throwable t) { // retry or show }2 }));3
  56. Being Reactive // onCreate disposables.add(um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override

    public void onNext(User user) { tv.setText(user.toString()); }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } })); // onDestroy disposables.dispose(); // button click listener disposables.add(um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { @Override public void onComplete() { // success! re-enable editing }1 @Override public void onError(Throwable t) { // retry or show }2 }));3 Push-based updates
  57. Being Reactive // onCreate disposables.add(um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override

    public void onNext(User user) { tv.setText(user.toString()); }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } })); // onDestroy disposables.dispose(); // button click listener disposables.add(um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { @Override public void onComplete() { // success! re-enable editing }1 @Override public void onError(Throwable t) { // retry or show }2 }));3 Push-based updates Declarative threading
  58. Being Reactive // onCreate disposables.add(um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override

    public void onNext(User user) { tv.setText(user.toString()); }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } })); // onDestroy disposables.dispose(); // button click listener disposables.add(um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { @Override public void onComplete() { // success! re-enable editing }1 @Override public void onError(Throwable t) { // retry or show }2 }));3 Push-based updates Declarative threading Easy error-handling
  59. Being Reactive // onCreate disposables.add(um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override

    public void onNext(User user) { tv.setText(user.toString()); }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } })); // onDestroy disposables.dispose(); // button click listener disposables.add(um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { @Override public void onComplete() { // success! re-enable editing }1 @Override public void onError(Throwable t) { // retry or show }2 }));3 Push-based updates Declarative threading Easy error-handling Specialized callbacks
  60. Being Reactive // onCreate disposables.add(um.getUser() .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<User>() { @Override

    public void onNext(User user) { tv.setText(user.toString()); }1 @Override public void onComplete() { /* ignored */ } @Override public void onError(Throwable t) { /* crash or show */ } })); // onDestroy disposables.dispose(); // button click listener disposables.add(um.setName("Jane Doe") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableCompletableObserver() { @Override public void onComplete() { // success! re-enable editing }1 @Override public void onError(Throwable t) { // retry or show }2 }));3 Push-based updates Declarative threading Easy error-handling Specialized callbacks Lifecycle cancelation
  61. Managing State Enter name: SUBMIT

  62. Managing State Enter name: SUBMIT Jane Doe

  63. Managing State Enter name: SUBMIT Jane Doe

  64. Managing State Enter name: SUBMIT

  65. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  66. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  67. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  68. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  69. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  70. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  71. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  72. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  73. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  74. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  75. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  76. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  77. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  78. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  79. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  80. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  81. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  82. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  83. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  84. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  85. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  86. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  87. Managing State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  88. Managing State

  89. Managing State RxView.clicks(…)

  90. Managing State doOnNext(…)

  91. Managing State getText()

  92. Managing State flatMap(…)

  93. Managing State flatMap(…)

  94. Managing State doOnNext(…)

  95. Managing State subscribe(…)

  96. Managing State

  97. Managing State

  98. Managing State

  99. Managing State RxView.clicks(…)

  100. Managing State doOnNext(…)

  101. Managing State doOnNext(…) doOnNext(…)

  102. Managing State doOnNext(…) doOnNext(…)

  103. Managing State getText()

  104. Managing State flatMap(…)

  105. Managing State doOnNext(…)

  106. Managing State subscribe(…)

  107. Managing State

  108. Managing State

  109. Managing State

  110. Managing State RxView.clicks(…) getText()

  111. Managing State RxView.clicks(…) getText() Observable<SubmitEvent>

  112. Managing State doOnNext(…) doOnNext(…) subscribe(…)

  113. Managing State doOnNext(…) doOnNext(…) doOnNext(…)

  114. Managing State doOnNext(…) doOnNext(…) doOnNext(…)

  115. Managing State doOnNext(…) doOnNext(…) subscribe(…)

  116. Managing State doOnNext(…) doOnNext(…) subscribe(…)

  117. Managing State subscribe(ignored -> …)

  118. Managing State subscribe((SubmitUiModel model) -> …) ignored

  119. Managing State

  120. Managing State

  121. Reactive State

  122. Reactive State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString())) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  123. Reactive State disposables.add(RxView.clicks(submitView) .doOnNext(ignored -> { submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(ignored

    -> service.setName(nameView.getText().toString()))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  124. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> nameView.getText().toString())Y .doOnNext(ignored -> { submitView.setEnabled(false);

    progressView.setVisibility(VISIBLE); }) .flatMap(name -> service.setName(name))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  125. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()))Y .doOnNext(ignored -> {

    submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(event -> service.setName(event.name))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); })); name
  126. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()))Y .doOnNext(ignored -> {

    submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(event -> service.setName(event.name))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  127. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()))Y .doOnNext(ignored -> {

    submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(event -> service.setName(event.name))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  128. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()))Y .doOnNext(ignored -> {

    submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(event -> service.setName(event.name))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  129. Reactive State final class SubmitUiModel { final boolean inProgress; final

    boolean success; final String errorMessage; private SubmitUiModel( boolean inProgress, boolean success, String errorMessage) { // ... } static SubmitUiModel inProgress() { /* ... */ } static SubmitUiModel success() { /* ... */ } static SubmitUiModel failure(String errorMessage) { /* ... */ } }
  130. Reactive State final class SubmitUiModel { final boolean inProgress; final

    boolean success; final String errorMessage; private SubmitUiModel( boolean inProgress, boolean success, String errorMessage) { // ... } static SubmitUiModel inProgress() { /* ... */ } static SubmitUiModel success() { /* ... */ } static SubmitUiModel failure(String errorMessage) { /* ... */ } }
  131. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .doOnNext(ignored -> {

    submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(event -> service.setName(event.name))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  132. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .doOnNext(ignored -> {

    submitView.setEnabled(false); progressView.setVisibility(VISIBLE); }) .flatMap(event -> service.setName(event.name))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  133. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .doOnNext(ignored -> {

    SubmitUiModel.inProgress() }) .flatMap(event -> service.setName(event.name))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); })); .startWith( submitView.setEnabled(false); progressView.setVisibility(VISIBLE);
  134. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .startWith(SubmitUiModel.inProgress()))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  135. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .startWith(SubmitUiModel.inProgress()))X .observeOn(AndroidSchedulers.mainThread()) .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  136. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  137. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  138. Reactive State final class SubmitUiModel { final boolean inProgress; final

    boolean success; final String errorMessage; private SubmitUiModel( boolean inProgress, boolean success, String errorMessage) { // ... } static SubmitUiModel inProgress() { /* ... */ } static SubmitUiModel success() { /* ... */ } static SubmitUiModel failure(String errorMessage) { /* ... */ } }
  139. Reactive State final class SubmitUiModel { final boolean inProgress; final

    boolean success; final String errorMessage; private SubmitUiModel( boolean inProgress, boolean success, String errorMessage) { // ... } static SubmitUiModel inProgress() { /* ... */ } static SubmitUiModel success() { /* ... */ } static SubmitUiModel failure(String errorMessage) { /* ... */ } }
  140. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .doOnNext(ignored -> progressView.setVisibility(GONE)) .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); }));
  141. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .map(response -> SubmitUiModel.success()) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .subscribe(s -> finish(), t -> { submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + t.getMessage(), LENGTH_SHORT).show(); })); progressView.setVisibility(GONE) .doOnNext(ignored -> )
  142. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .subscribe(s -> finish(), t -> {})); submitView.setEnabled(true); Toast.makeText(this, "Failed to set name: " + , LENGTH_SHORT).show();
  143. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .subscribe(s -> finish(), t -> {}));
  144. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .subscribe(s -> finish(), t -> {}));
  145. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.errorMessage, LENGTH_SHORT).show(); } }, t -> {})); s
  146. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.errorMessage, LENGTH_SHORT).show(); }X }, t -> {}));
  147. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()))X .subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.errorMessage, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  148. Reactive State disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())) .flatMap(event -> service.setName(event.name)

    .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())) .subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.errorMessage, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  149. Reactive State Observable<SubmitEvent> events = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()));

    disposables.add(events.flatMap(event -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())) .subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.errorMessage, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  150. Reactive State Observable<SubmitEvent> events = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()));

    Observable<SubmitUiModel> models = events .flatMap(event -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(models.subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.errorMessage, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  151. Reactive State Observable<SubmitEvent> events = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()));

    ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(events.compose(submit).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.errorMessage, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); })); models models
  152. Reactive State Observable<SubmitEvent> events = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()));

    ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(events.compose(submit).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.errorMessage, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  153. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event

    -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())); .compose(submit) .subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.errorMessage, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  154. Reactive State

  155. Reactive State

  156. Reactive State

  157. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event

    -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())); .compose(submit) .subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.message, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  158. Reactive State Observable<SubmitEvent> events = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()));

    ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(events.compose(submit).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.message, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  159. Reactive State Observable<SubmitEvent> events = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString()));

    ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(events.compose(submit).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.message, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  160. Reactive State class SubmitEventZ{ /* ... */ } Observable<SubmitEvent> events

    = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())); ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage()) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(events.compose(submit).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.message, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  161. Reactive State abstract class SubmitUiEvent {} class SubmitEventZextends SubmitUiEventY{ /*

    ... */ } class CheckNameEvent extends SubmitUiEvent { /* ... */ } Observable<SubmitEvent> submitEvents = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())); e
  162. Reactive State abstract class SubmitUiEvent {} class SubmitEventZextends SubmitUiEventY{ /*

    ... */ } class CheckNameEvent extends SubmitUiEvent { /* ... */ } Observable<SubmitEvent> submitEvents = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())); Observable<CheckNameEvent> checkNameEvents = RxTextView.afterTextChanges(nameView) .map(text -> new CheckNameEvent(text));
  163. Reactive State abstract class SubmitUiEvent {} class SubmitEventZextends SubmitUiEventY{ /*

    ... */ } class CheckNameEvent extends SubmitUiEvent { /* ... */ } Observable<SubmitEvent> submitEvents = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())); Observable<CheckNameEvent> checkNameEvents = RxTextView.afterTextChanges(nameView) .map(text -> new CheckNameEvent(text)); Observable<SubmitUiEvent> events = Observable.merge(submitEvents, checkNameEvents);
  164. Reactive State Observable<SubmitUiEvent> events = /* ... */; ObservableTransformer<SubmitEvent, SubmitUiModel>

    submit = events -> events .flatMap(event -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(events.compose(submit).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.message, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); })); abstract class SubmitUiEvent {} O b s e r v a b l e . m e r g e ( s u b m i t E v e n t s , c h e c k N a m e E v e n t s ) class SubmitEventZextends SubmitUiEventY{ /* ... */ } class CheckNameEvent extends SubmitUiEvent { /* ... */ } Observable<SubmitEvent> submitEvents = RxView.clicks(submitView) .map(ignored -> new SubmitEvent(nameView.getText().toString())); Observable<CheckNameEvent> checkNameEvents = RxTextView.afterTextChanges(nameView) .map(text -> new CheckNameEvent(text));
  165. Reactive State Observable<SubmitUiEvent> events = /* ... */; ObservableTransformer<SubmitEvent, SubmitUiModel>

    submit = events -> events .flatMap(event -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); disposables.add(events.compose(submit).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.message, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  166. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event

    -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress()));
  167. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event

    -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); ObservableTransformer<CheckNameEvent, SubmitUiModel> checkName = events -> events .switchMap(event -> event .delay(200, MILLISECONDS, AndroidSchedulers.mainThread()) .flatMap(event -> service.checkName(event.name)) .map(response -> ???) .onErrorReturn(t -> ???) .observeOn(AndroidSchedulers.mainThread()) .startWith(???));
  168. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event

    -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); ObservableTransformer<CheckNameEvent, SubmitUiModel> checkName = events -> events .switchMap(event -> event .delay(200, MILLISECONDS, AndroidSchedulers.mainThread()) .flatMap(event -> service.checkName(event.name)) .map(response -> ???) .onErrorReturn(t -> ???) .observeOn(AndroidSchedulers.mainThread()) .startWith(???));
  169. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event

    -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); ObservableTransformer<CheckNameEvent, SubmitUiModel> checkName = events -> events .switchMap(event -> event .delay(200, MILLISECONDS, AndroidSchedulers.mainThread()) .flatMap(event -> service.checkName(event.name)) .map(response -> ???) .onErrorReturn(t -> ???) .observeOn(AndroidSchedulers.mainThread()) .startWith(???));
  170. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events .flatMap(event

    -> service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); ObservableTransformer<CheckNameEvent, SubmitUiModel> checkName = events -> events .switchMap(event -> event .delay(200, MILLISECONDS, AndroidSchedulers.mainThread()) .flatMap(event -> service.checkName(event.name)) .map(response -> ???) .onErrorReturn(t -> ???) .observeOn(AndroidSchedulers.mainThread()) .startWith(???));
  171. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */;
  172. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> ???;
  173. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> ???; events
  174. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> ???; events checkName submit
  175. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> ???; events models checkName submit
  176. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> ???; events models checkName submit Observable.merge(…)
  177. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> ???; events models checkName submit Observable.merge(…) Observable.publish(…);
  178. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> ???; Observable.merge(…) Observable.publish(…);
  179. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> events.publish(shared -> Observable.merge( );Z …) Observable …ZG
  180. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> events.publish(shared -> Observable.merge( shared.ofType(SubmitEvent.class).compose(submit), shared.ofType(CheckNameEvent.class).compose(checkName)));
  181. Reactive State Observable<SubmitUiEvent> events = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel>

    submitUi = events -> events.publish(shared -> Observable.merge( shared.ofType(SubmitEvent.class).compose(submit), shared.ofType(CheckNameEvent.class).compose(checkName))); disposables.add(events.compose(submitUi).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.message, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); })); ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent, SubmitUiModel> checkName = /* ... */;
  182. Reactive State Observable<SubmitUiEvent> events = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel>

    submitUi = events -> events.publish(shared -> Observable.merge( shared.ofType(SubmitEvent.class).compose(submit), shared.ofType(CheckNameEvent.class).compose(checkName))); disposables.add(events.compose(submitUi).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.message, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); }));
  183. Reactive State Observable<SubmitUiEvent> events = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel>

    submitUi = events -> events.publish(shared -> Observable.merge( shared.ofType(SubmitEvent.class).compose(submit), shared.ofType(CheckNameEvent.class).compose(checkName))); disposables.add(events.compose(submitUi).subscribe(model -> { submitView.setEnabled(!model.inProgress); progressView.setVisibility(model.inProgress ? VISIBLE : GONE); if (!model.inProgress) { if (model.success) finish() else Toast.makeText(this, "Failed to set name: " + model.message, LENGTH_SHORT).show(); }X }, t -> { throw new OnErrorNotImplementedException(t); })); ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent, SubmitUiModel> checkName = /* ... */;
  184. Reactive State

  185. Reactive State

  186. Reactive State Observable<UiEvent>

  187. Reactive State Observable<UiEvent> Observable<UiModel>

  188. Reactive State Observable<UiEvent> Observable<UiModel> Observable<UiEvent>

  189. Reactive State Observable<UiEvent> Observable<UiModel> Observable<UiEvent> Observable<UiModel>

  190. Reactive State Observable<UiEvent> Observable<UiModel> Observable<Action> Observable<UiModel> UiEventZ

  191. Reactive State Observable<UiEvent> Observable<UiModel> Observable<Action> Observable<Result> UiModelZ

  192. Reactive State Observable<UiEvent> Observable<UiModel> Observable<Action> Observable<Result>

  193. Reactive State Observable<UiEvent> Observable<UiModel> Observable<Action> Observable<Result>

  194. Reactive State Observable<UiEvent> Observable<UiModel> Observable<Action> Observable<Result>

  195. Reactive State final class SubmitUiModel { final boolean inProgress; final

    boolean success; final String errorMessage; private SubmitUiModel( boolean inProgress, boolean success, String errorMessage) { // ... } static SubmitUiModel inProgress() { /* ... */ } static SubmitUiModel success() { /* ... */ } static SubmitUiModel failure(String errorMessage) { /* ... */ } }
  196. Reactive State inProgress false success false errorMessage null

  197. Reactive State inProgress false success false errorMessage null CheckNameResult.IN_FLIGHT

  198. Reactive State inProgress false success false errorMessage null CheckNameResult.IN_FLIGHT inProgress

    true success false errorMessage null CheckNameResult.SUCC
  199. Reactive State rogress se cess se orMessage l CheckNameResult.IN_FLIGHT inProgress

    true success false errorMessage null CheckNameResult.SUCCESS i f s f e n
  200. Reactive State ckNameResult.IN_FLIGHT inProgress true success false errorMessage null CheckNameResult.SUCCESS

    inProgress false success false errorMessage null Ch
  201. Reactive State rogress e cess se orMessage l CheckNameResult.SUCCESS inProgress

    false success false errorMessage null CheckNameResult.IN_FLIGHT i t s f e n
  202. Reactive State ckNameResult.SUCCESS inProgress false success false errorMessage null CheckNameResult.IN_FLIGHT

    inProgress true success false errorMessage null Su
  203. Reactive State nProgress alse uccess alse rrorMessage ull CheckNameResult.IN_FLIGHT inProgress

    true success false errorMessage null SubmitResult.IN_FLIGHT i t s f e n
  204. Reactive State eckNameResult.IN_FLIGHT inProgress true success false errorMessage null SubmitResult.IN_FLIGHT

    inProgress true success false errorMessage null Su
  205. Reactive State T inProgress true success false errorMessage null SubmitResult.IN_FLIGHT

    inProgress true success false errorMessage null SubmitResult.SUCCESS i f s t e n
  206. Reactive State SubmitResult.IN_FLIGHT inProgress true success false errorMessage null SubmitResult.SUCCESS

    inProgress false success true errorMessage null
  207. Reactive State SubmitUiModel initialState = SubmitUiModel.idle();

  208. Reactive State SubmitUiModel initialState = SubmitUiModel.idle(); Observable<Result> results = /*

    ... */;
  209. Reactive State SubmitUiModel initialState = SubmitUiModel.idle(); Observable<Result> results = /*

    ... */; Observable<SubmitUiModel> uiModels = results .scan(initialState, (state, result) -> /* ... */);
  210. Reactive State SubmitUiModel initialState = SubmitUiModel.idle(); Observable<Result> results = /*

    ... */; Observable<SubmitUiModel> uiModels = results .scan(initialState, (state, result) -> { if (result == CheckNameResult.IN_FLIGHT || result == SubmitResult.IN_FLIGHT) return SubmitUiModel.inProgress(); if (result == CheckNameResult.SUCCESS) return SubmitUiModel.idle(); if (result == SubmitResult.SUCCESS) return SubmitUiModel.success(); // TODO handle check name and submit failures... throw new IllegalArgumentException("Unknown result: " + result); }); /* ... */
  211. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = events -> events.flatMap(event ->

    service.setName(event.name) .map(response -> SubmitUiModel.success()) .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage())) .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); ObservableTransformer<CheckNameEvent, SubmitUiModel> checkName = events -> events.switchMap(event -> event .delay(200, MILLISECONDS, AndroidSchedulers.mainThread()) .flatMap(event -> service.checkName(event.name)) .map(response -> ???) .onErrorReturn(t -> ???) .observeOn(AndroidSchedulers.mainThread()) .startWith(???));
  212. Reactive State ObservableTransformer<SubmitAction, SubmitUiModel> submit = actions -> actions.flatMap(action ->

    service.setName(action.name) .map(response -> SubmitUiModel.success())A .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage()))B .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); ObservableTransformer<CheckNameAction, SubmitUiModel> checkName = actions -> actions.switchMap(action -> action .delay(200, MILLISECONDS, AndroidSchedulers.mainThread()) .flatMap(action -> service.checkName(action.name)) .map(response -> ???)Y .onErrorReturn(t -> ???)Z .observeOn(AndroidSchedulers.mainThread()) .startWith(???)); SubmitEvent SubmitUiModel events events event event CheckNameEvent SubmitUiModel events events event event event event
  213. Reactive State ObservableTransformer<SubmitAction, SubmitUiModel> submit = actions -> actions.flatMap(action ->

    service.setName(action.name) .map(response -> SubmitUiModel.success())A .onErrorReturn(t -> SubmitUiModel.failure(t.getMessage()))B .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitUiModel.inProgress())); ObservableTransformer<CheckNameAction, SubmitUiModel> checkName = actions -> actions.switchMap(action -> action .delay(200, MILLISECONDS, AndroidSchedulers.mainThread()) .flatMap(action -> service.checkName(action.name)) .map(response -> ???)Y .onErrorReturn(t -> ???)Z .observeOn(AndroidSchedulers.mainThread()) .startWith(???));
  214. Reactive State ObservableTransformer<SubmitAction, SubmitResult> submit = actions -> actions.flatMap(action ->

    service.setName(action.name) .map(response -> SubmitResult.SUCCESS)A .onErrorReturn(t -> SubmitResult.failure(t.getMessage()))B .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitResult.IN_FLIGHT)); ObservableTransformer<CheckNameAction, CheckNameResult> checkName = actions -> actions.switchMap(action -> action .delay(200, MILLISECONDS, AndroidSchedulers.mainThread()) .flatMap(action -> service.checkName(action.name)) .map(response -> CheckNameResult.SUCCESS)Y .onErrorReturn(t -> CheckNameResult.failure(t.getMessage()))Z .observeOn(AndroidSchedulers.mainThread()) .startWith(CheckNameResult.IN_FLIGHT)); SubmitUiModel.success() SubmitUiModel.failure(t.getMessage()) SubmitUiModel.inProgress() ??? ??? ???
  215. Reactive State ObservableTransformer<SubmitAction, SubmitResult> submit = actions -> actions.flatMap(action ->

    service.setName(action.name) .map(response -> SubmitResult.SUCCESS)A .onErrorReturn(t -> SubmitResult.failure(t.getMessage()))B .observeOn(AndroidSchedulers.mainThread()) .startWith(SubmitResult.IN_FLIGHT)); ObservableTransformer<CheckNameAction, CheckNameResult> checkName = actions -> actions.switchMap(action -> action .delay(200, MILLISECONDS, AndroidSchedulers.mainThread()) .flatMap(action -> service.checkName(action.name)) .map(response -> CheckNameResult.SUCCESS)Y .onErrorReturn(t -> CheckNameResult.failure(t.getMessage()))Z .observeOn(AndroidSchedulers.mainThread()) .startWith(CheckNameResult.IN_FLIGHT));
  216. None
  217. Observable<UiEvent>

  218. Observable<UiEvent> Observable<Action>

  219. Observable<UiEvent> Observable<Action> Observable<Result>

  220. Observable<UiEvent> Observable<Action> Observable<Result> Observable<UiModel>

  221. Observable<UiEvent> Observable<Action> Observable<Result> Observable<UiModel> Log.d

  222. Observable<UiEvent> Observable<Action> Observable<Result> Observable<UiModel> Log.d

  223. Observable<UiEvent> Observable<Action> Observable<Result> Observable<UiModel> Subject<UiEvent> TestObserver<UiModel>

  224. Observable<UiEvent> Observable<Action> Observable<Result> Observable<UiModel>

  225. Observable<UiEvent> Observable<UiModel> TestObserver<UiEvent> Subject<UiModel>

  226. Observable<UiEvent> Observable<Action> Observable<Result> Observable<UiModel>

  227. Observable<UiEvent> Observable<Action> Observable<Result> Observable<UiModel> Redux / Cycle

  228. jakewharton jakewharton jakewharton twitter.com/ github.com/ .com Managing State with RxJava