All Ruby on Rails Node JS Android iOS React Native Frontend Flutter QA

Android + Coroutines = ❤️ in 2020?

Over the past few years, RxJava along with RxKotlin extensions was considered a first-class choice when it comes to asynchronous programming on Android. However, since Kotlin Coroutines API was announced stable it’s considered a more lightweight and exciting alternative to Rx more and more often. Google officially admits it is all hands for using coroutines on production as it helps to scale execution of async jobs on Android OS efficiently.  

But still, at the same time, Google promotes using LiveData with ViewModel as the state-of-the-art pattern for Android app architecture and maintaining objects which are able to survive screen configuration changes (e.g. screen rotation). Having three different APIs: LiveData, Coroutines, and Flow can make you feel a bit overwhelming. Luckily, as explained at Android Dev Summit 2019, there are useful extensions going to be added to the Jetpack’s lifecycle-viewmodel-ktx and lifecycle-livedata-ktx packages supposed to help with integrating those APIs seamlessly. We have just tested them out in action and here are the five cool examples of what you can do them.

1. Auto cancelation with lifecycleScope and viewModelScope

Having the ViewModel class whose instances are able to survive screen configuration changes makes it necessary to handle Coroutines cancelation the smart way. Obviously, we don’t want to call the coroutine cancel() function manually when ViewModel.onCleared() is invoked. It’s not smart enough. Instead, we want the coroutine to cancel automatically when the view model is no longer going to exist. This is the case where the extensions like viewModelScope and lifecycleScope can help.

The viewModelScope is an extension property of the ViewModel class which provides a CoroutineScope instance bounded to the life scope of the view model instance. In order to start the auto-cancellable coroutine within that scope just use the launch() or async() function as follows:

viewModelScope.launch(Dispatchers.IO) { doSomeLongRunningJob() }

Similarly, the lifecycleScope is an extension property of the LifecycleOwner instance (e.g. Activity or Fragment), and it’s bounded to the Lifecycle of the Activity or Fragment.

2. Associating coroutine launch time with Lifecycle state

The main advantage of the lifecycleScope CoroutineScope is its built-in cancelation mechanism which cancels the coroutine on the lifecycle Destroy event.

lifecycleScope.launch { doSomeLongRunningJob() }

In addition to auto-canceling, LifecycleCoroutineScope provides also three more functions which are helpful when it comes to scheduling coroutine start time. Apart from the basic LifecycleCoroutineScope.launch(), which executes the coroutine immediately, there are the following functions available:

  • LifecycleCoroutineScope.launchWhenCreated()

It launches when the Lifecycle controlling this LifecycleCoroutine scope is at least in Lifecycle.State.CREATED state.

  • LifecycleCoroutineScope.launchWhenStarted()

It launches when the Lifecycle controlling this LifecycleCoroutine scope is at least in Lifecycle.State.STARTED state.

  • LifecycleCoroutineScope.launchWhenResumed()

It launches when the Lifecycle controlling this LifecycleCoroutine scope is at least in Lifecycle.State.RESUMED state.

For example, the code snippet below will launch the coroutine when the Lifecycle of an Activity is in Lifecycle.State.RESUMED state. It will run until the coroutine block completes or the Lifecycle is destroyed.

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    lifecycleScope.launchWhenResumed {
      doSomeLongRunningJob()
    }
  }
}

3. Creating LiveData instance emitting values inside a Coroutine block

LiveData is often used to expose emitted values from view models to the views. In case we’d like to start an asynchronous coroutine job that emits some State values to the LiveData we can use the code like this:

val myLiveData: LiveData<State> = MutableLiveData<State>().apply {
  viewModelScope.launch {
    this@apply.postValue(State.Loading)
    val result = longRunningTask()
    this@apply.postValue(result)
  }
}

And with the liveData builder we can simplify it to the following lines:

val myLiveData: LiveData<State> = liveData<State> {
  emit(State.Loading)
  emit(longRunningTask())
}

It’s also worth noting the liveData function can take one of the Dispatchers as an argument to dispatch the job on it and timeoutInMs - the timeout in ms before canceling the block if there are no active observers.

4. Easy Flow to LiveData converting

Are you working with Kotlin Flow API and want to convert it to LiveData instance? Now you can simply do it with the Flow.asLiveData() extension function. That’s it!

5. Representing callback-style API as a Coroutine suspend function

Coroutines are great when it comes to async programming because they allow writing the non-blocking code in a natural way. However, sometimes we are forced to work with the old school callback-style async code. That’s often a case when we are dealing with old Java SDKs and APIs. So how we can effectively merge the callbacks with the coroutines code? This is the case where the uspendCancellableCoroutine() function from the Kotlin standard library can help! Let’s take a look at how we can use it in action.

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

The lambda block passed to the suspendCancellableCoroutine function exposes an instance of CancellableContinuation which you can access in order to emit the result or exception within the coroutine. Long story short, it allows converting callbacks into coroutine suspend function syntax.

Summary

Still not sure about giving the Coroutines a try in your app? You should not hesitate any more!

After the Coroutines framework was announced stable Google decided to promote it as a first-class choice for async programming on Android. And the new Lifecycle and ViewModel ktx extensions are definitely going to bring more joy to the async programming with coroutines.

You can watch the full talk from Android Dev Summit 2019 on Google's vision of using Android’s LiveData along with Coroutines and Flow. Would you like to see some code instead? Here you are.

New call-to-action
Looking for new opportunities? Check our offers!
READ ALSO FROM Kotlin
Read also
Need a successful project?
Estimate project or contact us