LiveData with Coroutines & Flow Cheat Sheet

As an Android developer we always wanted to avoid life cycles and not choose scope for start and stop operations, instead we want this to be handled gracefully by the underlying architecture.

Why we like coroutines
1. Easy to get off the main thread
2. Minimal boilerplate
3. Structured concurrency = managed jobs

Launching coroutines

ViewModel:
init {
viewModelScope.launch {
//start
}
}
View:
lifeCycleScope.launch {
//run
}
lifeCycleScope.launchWhenResumed {
//run
}
lifeCycleScope.launchWhenStarted {
//run
}

Points:
1. Use work manager for jobs like persisting on a server (tweet to server)
2. Use Application scope for local operation like clean local db

ViewModel + LiveData

private val _result = MutableLiveData<String>()
val result : LiveData<String> = _result
init {
viewModelScope.launch {
val computationResult = doComputation()
_result.value = computationResult
}
}

LiveData coroutine builder

class MyViewModel {
val result = liveData {
emit(doComputation())
}
}

Cancelling coroutines
1. All suspending functions in kotlinx.coroutines are cancellable.
2. isActive property is to check if coroutine is active, and you can cancel on verify.

One-shot operation vs multiple values (observers)

OneShot Operation
View → VM(LiveData) → Repository(suspend fun) → DataSource(suspend fun)

Multiple values With Observers
View -> VM(LiveData) → Respository(LiveData) → DataSource(LiveData)

Streams with Flow
View -> VM(LiveData) → Respository(Flow) → DataSource(Flow)

LiveData to LiveData

val currentWeather : LiveData<String> =
dataSource.fetchWeather()

Flow to LiveData

dataSource.fetchWeatherFlow().asLiveData()

emit 1 item then emit n items (emit 1 + emit n)

dataSource.fetchWeatherFlow()
.onStart { emit(LOADING_STRING) }
.asLiveData()

Points
1. In LiveData .map will run on main thread and we need .switchMap to make it suspend fun
2. In Flow map will make it suspend fun

Points
1. Retrofit supports coroutines from 2.6.0
2. Room supports coroutines from 2.1.0

suspendCancellableCoroutine
Acts as an adapter between coroutine world and callback world

Retrofit example with suspendCancellableCoroutine

suspend fun doOneShot(param : String) : Result<String> =
suspendCancellableCoroutine { continuation ->
api.addOnCompleteListener { result ->
continuation.resume(result)
}.addOnFailureListener { error ->
continuation.resumeWithException(error)
}.fetchSomething(param)
}

Note: resume in the above is ignored in cancelled state