返回

Reactive State Restoration with MVI

Android

In previous posts, we discussed the power of Model-View-Intent (MVI) and unidirectional data flow, and how it drastically simplifies state restoration. In this article, we will dive deeper into how this works.

The scenario here is simple. We want our RxJava streams to persist over time across Android lifecycle events, so that they can be recreated and continue emitting values when the Activity/Fragment is recreated (e.g. on configuration changes).

RxJava Streams and Android Lifecycle

By default, RxJava streams are not designed to persist across Android lifecycle events. When an Activity or Fragment is destroyed (e.g. on rotation), all its associated RxJava subscriptions are automatically disposed. This means that any state held in these streams will be lost.

To solve this, we need a way to recreate the streams and restore their state when the Activity/Fragment is recreated. This is where Android Lifecycle components come into play.

Android Lifecycle Components

Android Lifecycle components provide a way to observe the lifecycle of an Activity or Fragment. They allow us to register callbacks that will be triggered at specific points in the lifecycle, such as onCreate, onStart, and onDestroy.

By observing the lifecycle events, we can create and dispose of our RxJava streams at the appropriate times. For example, we can create the streams in onCreate and dispose of them in onDestroy.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // Create the RxJava stream
    val stream = Observable.interval(1000, TimeUnit.MILLISECONDS)

    // Subscribe to the stream and update the UI
    stream.subscribe { count ->
        // Update the UI with the current count
    }
}

override fun onDestroy() {
    super.onDestroy()

    // Dispose of the stream
    stream.dispose()
}

State Restoration with MVI

In an MVI architecture, the state of the app is held in a single, immutable object called the ViewState. The ViewState is then used to update the UI.

To restore the state when the Activity/Fragment is recreated, we can simply recreate the ViewState from the saved instance state. This is done by implementing the onSaveInstanceState and onRestoreInstanceState methods in the Activity/Fragment.

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)

    // Save the ViewState to the saved instance state
    outState.putParcelable("viewState", viewState)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    super.onRestoreInstanceState(savedInstanceState)

    // Restore the ViewState from the saved instance state
    val viewState = savedInstanceState?.getParcelable<ViewState>("viewState")

    // Update the UI with the restored ViewState
    updateUi(viewState)
}

Conclusion

State restoration in an MVI architecture is relatively straightforward. By using Android Lifecycle components to observe lifecycle events and recreate RxJava streams, we can ensure that the state of our app is preserved across configuration changes.