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

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

Jake Wharton

March 21, 2017
Tweet

More Decks by Jake Wharton

Other Decks in Programming

Transcript

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

    a single asynchronous source breaks imperative programming.
  2. Why Reactive? interface UserManager { User getUser(); void setName(String name);

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

    void setAge(int age);
 }A UserManager um = new UserManager(); System.out.println(um.getUser());
  4. 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");
  5. 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());
  6. Why Reactive? interface UserManager { User getUser(); void setName(String name);

    // <-- now async void setAge(int age); // <-- now async
 }A
  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()); um.setName("Jane Doe"); System.out.println(um.getUser());
  8. Why Reactive? interface UserManager { User getUser(); void setName(String name,

    Runnable callback); void setAge(int age, Runnable callback);
 }A
  9. 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()); });
  10. 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
  11. 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 });
  12. 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
  13. 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 });
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  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() { 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
  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() { 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
  21. Why Reactive? GET / 200 OK SELECT * Jane Doe

    setText onClick SELECT * Jane Doe setText UPDATE user Jane Doe setText
  22. Why Reactive? Unless you can model your entire system synchronously,

    a single asynchronous source breaks imperative programming.
  23. 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 */ } });
  24. 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
  25. 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
  26. 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();
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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(); }));
  36. 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(); }));
  37. 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(); }));
  38. 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(); }));
  39. 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(); }));
  40. 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(); }));
  41. 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(); }));
  42. 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(); }));
  43. 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(); }));
  44. 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(); }));
  45. 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(); }));
  46. 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(); }));
  47. 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(); }));
  48. 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(); }));
  49. 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(); }));
  50. 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(); }));
  51. 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(); }));
  52. 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(); }));
  53. 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(); }));
  54. 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(); }));
  55. 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(); }));
  56. 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(); }));
  57. 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(); }));
  58. 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(); }));
  59. 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(); }));
  60. 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(); }));
  61. 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
  62. 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(); }));
  63. 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(); }));
  64. 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(); }));
  65. 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) { /* ... */ } }
  66. 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) { /* ... */ } }
  67. 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(); }));
  68. 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(); }));
  69. 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);
  70. 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(); }));
  71. 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(); }));
  72. 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(); }));
  73. 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(); }));
  74. 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) { /* ... */ } }
  75. 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) { /* ... */ } }
  76. 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(); }));
  77. 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 -> )
  78. 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();
  79. 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 -> {}));
  80. 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 -> {}));
  81. 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
  82. 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 -> {}));
  83. 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); }));
  84. 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); }));
  85. 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); }));
  86. 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); }));
  87. 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
  88. 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); }));
  89. 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); }));
  90. 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); }));
  91. 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); }));
  92. 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); }));
  93. 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); }));
  94. 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
  95. 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));
  96. 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);
  97. 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));
  98. 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); }));
  99. 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()));
  100. 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(???));
  101. 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(???));
  102. 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(???));
  103. 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(???));
  104. Reactive State ObservableTransformer<SubmitEvent, SubmitUiModel> submit = /* ... */; ObservableTransformer<CheckNameEvent,

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

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

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

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

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

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

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

    SubmitUiModel> checkName = /* ... */; ObservableTransformer<SubmitUiEvent, SubmitUiModel> submitUi = events -> events.publish(shared -> Observable.merge( );Z …) Observable …ZG
  112. 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)));
  113. 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 = /* ... */;
  114. 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); }));
  115. 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 = /* ... */;
  116. 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) { /* ... */ } }
  117. 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
  118. 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
  119. 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
  120. 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
  121. Reactive State SubmitUiModel initialState = SubmitUiModel.idle(); Observable<Result> results = /*

    ... */; Observable<SubmitUiModel> uiModels = results .scan(initialState, (state, result) -> /* ... */);
  122. 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); }); /* ... */
  123. 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(???));
  124. 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
  125. 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(???));
  126. 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() ??? ??? ???
  127. 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));