Skip to content

PoC using Flow completely on an Android Projet with MVVM architecture. No LiveData. The advantage over simply using the viewModelScope is the fact that 5 seconds after leaving the application (not killing it !), the coroutine is cancelled, avoid possibly unnecessary work.

Notifications You must be signed in to change notification settings

NinoDLC/Kotlin_Flow_To_The_View

Repository files navigation

Kotlin_Flow_To_The_View

PoC using Flow to the View on an Android Project. DO NOT expose a Flow to the View. Use LiveData to communicate between ViewModel and View instead.

TL;DR :

Flow with .asLiveData()
or liveData {}
Flow with .stateIn() viewModelScope.launch() with MutableLiveData field
ViewModelScope ✔️ ✔️ ✔️
Lifecycle ✔️
Suspended after Activity is stopped for 5s (default)
✔️
Suspended after SharingStarted's timeout

Resources will be wasted (but no crash) as soon as Activity is stopped
Republish content on rebind ✔️ No ❌ Yes (need distinctUntilChanged() before collect on View) ✔️ No
Adjustable Timeout ✔️
timeoutInMs parameter
✔️
stopTimeoutMillis of SharingStarted.WhileSubscribed value for started parameter
On timeout (5s) behavior Restarts only if Flow didn't complete¹ Restarts only if Flow didn't complete¹
or always exposes initial state and restarts Flow (with replayExpirationMillis = 0)
Adjustable Initial state ✔️
Must expose initial state manually (can use latestValue to check if a previous value has been emitted)
✔️✔️
Initial state is exposed automatically when coroutine is restarted
Dynamic Initial state ✔️
Can change arbitrarily

Initial state can't change between timeouts
Unit Testing ✔️
Must inject Dispatchers
Must use TestCoroutineScope & InstantTaskExecutorRule
✔️
Must inject Dispatchers and SharingStarted strategy
Must use TestCoroutineScope
✔️
Must inject Dispatchers
Must use TestCoroutineScope & InstantTaskExecutorRule
Boilerplate ✔️✔️
2 lines:
field .asLiveData(dispatchers.io)
or liveData(dispatchers.io) {}
injected Dispatchers

4 to 6 lines:
.stateIn()
shared parameter
scope parameter
initial value parameter
injected Dispatchers
injected SharingStarted strategy

4 to 5 lines:
private MutableLiveData field
LiveData getter
init {}
viewModelScope.launch
injected Dispatchers
Complexity / Error prone ✔️ ✔️
LiveData dependency ✔️

¹ : If a hot flow (MutableStateFlow, MutableSharedFlow, Channel, etc...) is used to produce values downstream, the flow will never complete !
This means the collect will restart after the 5s timeout, even if it would seem unnecessary.
More information here

Using .asLiveData() extension, liveData() function or .stateIn() extension is a better approach than simply launching on the viewModelScope to publish to a MutableLiveData or MutableStateFlow. This is because when using .asLiveData(), liveData() or stateIn(), the coroutine is cancelled 5 seconds after leaving the Activity (not killing the application, just pressing 'home' or 'recent apps' button !), avoiding possibly unnecessary work.

There is one way to go with pure Kotlin, meaning there's no need for LiveData dependencies, but I wouldn't recommend it at the time, because any hot flow (see ¹) will make your collection restart after a timeout. Using a MutableLiveData with a switchMap() operator to "trigger" a flow is the only viable option to this day. Also, unit testing is a bit more complicated (one needs to inject both Dispatchers and SharingStarted strategy). And, to my opinion, it's more error prone.

See for yourself in the project, both "pure Kotlin" and LiveData approach are used !

Inspired by : https://medium.com/androiddevelopers/a-safer-way-to-collect-flows-from-android-uis-23080b1f8bda

About

PoC using Flow completely on an Android Projet with MVVM architecture. No LiveData. The advantage over simply using the viewModelScope is the fact that 5 seconds after leaving the application (not killing it !), the coroutine is cancelled, avoid possibly unnecessary work.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages