The glue

Save State By Using RxJava or Kotlin Flow

Hadi Lashkari Ghouchani

--

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.

RxViewModelSample.kt

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.

--

--