In this series, I translated a series of articles of coroutines and Flow developers, aiming to understand the reasons for the current design of coroutines, flows and LiveData, find out their problems from the perspective of designers, and how to solve these problems. PLS Enjoy it.

LiveData was introduced by Google as a means of connecting an Activity/Fragment to its ViewModel. Like a simplified reactive component (such as RxJava or Kotlin’s Flow), it also knows the view’s life cycle.

With the recent release of Kotlin’s Coroutine and Kotlin’s Flow, Google now offers a way to connect Kotlin’s Flow and LiveData using LiveData Coroutine Builder.

Benefits of using Kotlin’s Coroutine/Flow

The advantage of using Kotlin’s Coroutine/Flow to connect to LiveData is that it ensures that the underlying components (such as repositories, domain layers) are completely done in the background.

This will help to overcome the following problems:

  • If the main thread is busy, the postValue of LiveData is discarded.
  • LiveData conversions are done in the main thread.

Pre-requisite

To use LiveData Coroutine Builder, first we need to include the KTX library, for example:

Implementation "androidx lifecycle: lifecycle - livedata - KTX: 2.4.0." "Copy the code

Then, people can use it, using:

liveData {
    emit(data) // OR
    emitSource(liveData)
}
// OR
stateFlow.asLiveData()
Copy the code

Let’s take a look at the use of LiveData Coroutine Builder.

1. Connect Kotlin Coroutine to LiveData

If we have a Coroutine that needs to be called to get some data into LiveData, we can do the following.

val someTypeLiveData: LiveData<SomeType> = liveData {
    // get data from is a suspend function 
    val data = aSuspedFunction() 
    emit(data)
}
Copy the code

Once LiveData is connected to any observer, it calls and accepts data from the suspend function.

We can also ensure that it is done on a background thread, using:

val someTypeLiveData: LiveData<SomeType> = liveData(viewModelScope.coroutineContext + Dispatchers.IO) { // get data from is a suspend function  val data = aSuspedFunction() emit(data) }Copy the code

2. Connect Kotlin Flow (or StateFlow) to LiveData

This is a one-time acquisition. But if we have a process in which the data is continuously discharged, we can use:

val someTypeLiveData: LiveData<SomeType> =   
    stateFlow.asLiveData(
        viewModelScope.coroutineContext + Dispatchers.IO
    )
Copy the code

Once LiveData is connected to any observer, it stands by on stateFlow and emits its data. Internally, asLiveData is actually a LiveData {… }.

public fun <T> Flow<T>.asLiveData(
    context: CoroutineContext = EmptyCoroutineContext,
    timeoutInMs: Long = DEFAULT_TIMEOUT
): LiveData<T> = liveData(context, timeoutInMs) {
    collect {
        emit(it)
    }
}
Copy the code

3. Transformation on Background

As we shared earlier, the LiveData transformation is done on the main thread. This makes the transformation a problem if the transformation logic is computationally intensive.

To move it to the background, we have to use LiveData’s switchMap and LiveData’s Coroutine Builder, as shown below.

val liveData: LiveData<String> =
    Transformations.switchMap(sourceliveData) {
        liveData(viewModelScope.coroutineContext + Dispatchers.IO)
            val data = someTranformFunction(it)
            emit(data)
        }
    }
Copy the code

4. Connecting Multiple LiveData Source Emission

In this case, where we have multiple LiveDatas sources and we want to control the logic of the data output, we can also use Coroutine Builder.

Here’s a very lame example:

liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
    emitSource(repository.liveDataSourceA)
    delay(2000)
    emitSource(repository.liveDataSourceB)
    delay(2000)
    emitSource(repository.liveDataSourceC)
}
Copy the code

Full example: github.com/elye/demo_a…

5. Delay and Keep Coroutine Alive Temporarily

One of our special features in liveData Coroutine Builder is that it can be configured to keep coroutine alive for certain periods of time when liveData is inactive.

This is useful in cases where the user changes the configuration or temporarily pauses the Activity, and we want to keep the loop alive to get the job done. However, if it exceeds the time threshold, then we want to restart the entire Coroutine operation.

Here is an accurate description of this condition:

The liveData building block acts as a structured concurrency component between Coroutines and liveData. This code block starts execution when LiveData becomes active and is automatically cancelled after a configurable timeout when LiveData becomes inactive. If it is cancelled before completion, it will be restarted if LiveData becomes active again. If it completes successfully in a previous run, it does not restart. Note that the restart will only happen if it is automatically cancelled. If the block is cancelled for any other reason (such as throwing a CancellationException), it will not be restarted.

If we look at the code, we’ll see that we have timeoutInMs, which defaults to 5s.

@OptIn(ExperimentalTypeInference::class)
public fun <T> liveData(
    context: CoroutineContext = EmptyCoroutineContext,
    timeoutInMs: Long = DEFAULT_TIMEOUT,
    @BuilderInference block: suspend LiveDataScope<T>.() -> Unit
): LiveData<T> = CoroutineLiveData(context, timeoutInMs, block)
Copy the code

This means that when we put the activity (observing LiveData) in the background and the activity is paused/stopped (note: not not retained activity), the Coroutine will remain active for the duration of timeoutInMs.

  • If the timeout completes before the Coroutine runs, the Coroutine will restart when the Activity resumes.

  • If the timeout is not complete, when the Activity resumes, it will continue until it completes, taking into account that the Coroutine is not complete.

  • If the coroutine completes before the timeout, the coroutine is not restarted, even if the Activity has not been resumed, but merely emits its last value.

TL; DR

With LiveData Coroutine Builder, we couldn’t bridge Kotlin Flow and LiveData if we wanted to. This gives us more flexibility to combine LiveData’s ability to observe the Android life cycle with Kotlin Flor’s better reactive operations and threading.

Medium.com/mobile-app-…

I would like to recommend my website xuyisheng. Top/focusing on Android-Kotlin-flutter welcome you to visit