Save State By Using RxJava or Kotlin Flow
Previously in my “RxJava instead of LiveData in MVVM” post, I proposed to use RxJava as io.reactivex.rxjava2
or Kotlin Coroutines as org.jetbrains.kotlinx:kotlinx-coroutines-core
instead of LiveData
in android.arch.lifecycle
. It relatively is a simple idea, but someone needs to take care of it. Now my solution supports saving data too, so let’s review it again.
To convince you that this approach is a good way to go let’s gather pros and cons of LiveData
implementation respect to RxJava and Kotlin Coroutines.
Pros
— It’s lifecycle-aware, which means it can subscribe and unsubscribe itself in the right moments of lifecycle of Activity
and Fragment
of Android.
— You can easily save the data by using androidx.lifecycle:lifecycle-viewmodel-savedstate
library.
Cons
— First of all, LiveData
has just one strategy on backpressure, which is dropping, so the values are missing out if you post
them fast into LiveData
. On the other hand, RxJava supports backpressure with different strategies. Also Kotlin Coroutines supports one strategy by suspending the send
method, which is a kind of buffering, but at least it’s not drop!
— Second, LiveData
has only one strategy to hold the values, but we have PublishProcessor
, BehaviorProcessor
, ReplayProcessor
, and etc. Or if you’re working in the main thread all the time, so have no plan to support backpressure, you can use PublishSubject
, BehaviorSubject
, ReplaySubject
, and etc. Also Kotlin Coroutines supports different strategies on holding the values such as UNLIMITED
, CONFLATED
, BUFFERED
, etc.
— Third, Both RxJava and Kotlin Flow
has a lot of operators that you may miss when you’re working with LiveData
.
— Forth, while you test LiveData
you miss some RxJava testing sugars, such as TestObserver
, TestScheduler
etc. The same about the Kotlin Flow
too.
Now that we know what is the pros and cons of LiveData
we can choose to support some features such as variety of operators, different strategy of holding values, etc. for LiveData
OR we ignore LiveData
and support being lifecycle-aware and saving data for RxJava and Kotlin Flow
. Here we choose the later solution as you can find it in
and
libraries.
Usage
In this post I’ll just focus on MVVM
architectural pattern and RxJava, but above solution can be used in any situation such as MVI
with Kotlin Multiplatform etc. Below is a sample ViewModel
which is using this approach.
As you can see the publisher
would not hold any value as the implementation of PublishProcessor
implies. So it would be good to use it for actions. To hide the instance of this PublishProcessor
, we wrap it up in a LifecycleAware
by using toLifecycleAware()
extension function, where you can find it in both above libraries.
On the other hand, the extendedPublisher
is a BehaviorProcessor
and good to keep the state of the view. So it would hold one state and the starting state is applied by .apply{ onNext("Test") }
. We keep the state so in case of configuration change of the Activity
or Fragment
don’t loose the state. Also if the Activity
tries to save its state by calling onSaveInstanceState()
, we need to save the value of extendedPublisher
. To do so, we use toExtendedLifecycleAware(KEY)
extension function to wrap BehaviorProcessor
in a ExtendedLifecycleAware
instance. This kind of objects can save and retrieve their states in case of onSaveInstanceState()
. However, you can find the implementation of toExtendedLifecycleAware(KEY)
in both of above libraries. Notice the KEY
is the same key that you need to save the state in the Bundle
and the type of the state must be savable in the Bundle
, so it can be any of Java primitives, a Parcelable
or a Bundle
, etc.
In MVVM
architectural pattern, we need to observe above publishers in Activity
or Fragment
.
As you can see it’s so easy to observe those wrapped up publishers. By the way, there are two things to notice. First, there are some extra parentheses that would help us to not thinking about passing Activity
or Fragment
to the observe()
method. They would be passed in an indirect way and make our life easier! The second thing is that the stringEmitter
would be observed after onStart
and before onStop
or onDestroy
, depends which one gets called sooner. But the extendedStringEmitter
would be observed after onStart
and before onStop
, onDestroy
or onSaveInstanceState
, depends which one is called sooner.
So with this solution, we also can solve the problem that I mentioned in “RxJava instead of LiveData in MVVM” post, which is about comitting a Fragment
after onSaveInstanceState
. To solve it you just need to use toExtendedLifecycleAware(KEY)
for your action of opening Fragment
. So if by above publisher
you want to open a Fragment
, instead of publisher.toLifecycleAware()
use publisher.toExtendedLifecycleAware(KEY)
and use different KEY
for each of your actions. AND problem solved! Notice after retrieving the action by this KEY
, it would be emitted in the publisher
, but because we first emit it then start the observation, the observer would not see the action again!
Don’t forget to clap here in Medium and give star in Github repositories if you liked them. So I can count the number of my followers that like RxJava vs Kotlin Coroutines! ;P Then send me some feedback if you enjoyed it.