David Liu
David Liu

Reputation: 9600

State machine using RxJava?

I'm trying to go all in on RxJava and solve this problem I have with it, but it seems very unsuited for it, as RxJava seems to not want to deal with having any sort of state, rather only passing along events and mutating them to handle them.

The basic state machine behavior I'm trying to emulate with RxJava is this:

  1. On app start event, wait for the next app pause.
  2. On app pause event, start a 15 minute timer and wait for the next app resume.
    • If the app resumes within the timer, cancel it and go back to step 1.
  3. On app resume event, if the 15 minute timer has elapsed, refresh and go back to step 1.

Upvotes: 2

Views: 2199

Answers (1)

Bob Dalgleish
Bob Dalgleish

Reputation: 8227

While it is possible to implement a state machine in RxJava, it gets very ugly, very quickly. You can use the switchMap() operator to select some of the branching paths, and you can use several other operators to branch and merge.

Most of the issue is that state machine transitions tend to look more like go to than structured programming, and they tend not to resemble functional programming at all.

For similar reasons, it is quite difficult to create a fluent or functional description of any non-trivial state machine. There is an RxJava implementation of general state machines RxFsm, and it looks quite competent. You will have provide timers as external inputs, and other such work-arounds.

Here is code that implements your state machine, using some RxJava elements, and making some simplifying assumptions:

Observable<AppState> appStateObservable;
AtomicReference<Subscription> timerSubscription = new AtomicReference<>(Subscriptions.empty());

appStateObservable
  .doOnNext( state -> if ( state == START ) doStartup() )
  .filter( state -> state != START )
  .subscribe( state -> if ( state == PAUSE ) {
      timerSubscription.set( Observable.timer(15, MINUTES)
        .subscribe( timerSubscription.get().unsubscribe() ) );
    } else  if ( timerSubscription.get().isUnsubscribed() ) {
      refresh();
    } else {
      timerSubscription.get().unsubscribe();
    } );

It uses the timerSubscription subscription state to determine if the timer has fired, and, thus, perform the refresh.

Upvotes: 1

Related Questions