Reactive State Restoration with MVI
2023-12-04 01:46:12
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.