Tag

Kotlin Coroutine

Browsing

Coroutines are one of the ūüėć nicest language features in Kotlin. They provide a rather headache-free way to use the power of concurrency in your Kotlin program. I was very excited when I started playing around with them but eventually¬†came across a problem: what if you have nested coroutines (like a parent-child coroutine).

For instance, I came across this problem when I wanted to launch a coroutine and with-in of that coroutine I want to start multiple coroutines and when I cancel the parent coroutine the children needs to be canceled as well. How!

Gradle Stuff

Kotlin coroutine requires us to add some more stuff to our application modules build.gradle file. We start by adding the following dependencies right after the android section:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0-RC1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0-RC1'

In this article, I assume that you’ve some elementary proficiency with Kotlin coroutine. Now instead of directly jumping to above¬†problem, I wanna start this article by just launching a simple coroutine and cancel it when the ViewModel is going to destroy.

class RepositoryViewModel constructor(private val movieRepo : MovieRepo) : ViewModel() {
   
   private var movieRepoJob : Job? = null    
   
   fun getMovies() {
      movieRepoJob = launch {
           movieRepo.getPopularMovies()
           .... update view state
      }   
   }

   override fun onCleared(){
      movieRepoJob?.cancel()
   }
   
}

You see that’s its a very simple example, we have a ViewModel and in that there’s a getMovies method who simply¬†launch¬†a coroutine. Well, actually the¬†launch¬†returns a¬†Job¬†and that¬†Job¬†allows you to cancel it while it’s been running. So, you can simply cancel the¬†coroutine in the onCleared¬†method of ViewModel. Simple isn’t it! ūü§ó

Parent-Child Async Tasks

Now let’s get back to our previous example where we have more than one async tasks running. We have one parent¬†launch¬†block and inside that block, there’s another¬†async¬†task is running. So, how do we keep track of those tasks effectively when our ViewModel is going off the screen or we need to cancel them. Using this solution our previous example would look like:

class MyViewModel constructor(private val apiService : ApiService) : ViewModel() {
   
   private var parentJob : Job? = null    
   private var firstChildJob : Job? = null
   private var secondChildJob : Job? = null
   
   fun getMovies() {
      parentJob = launch {
           apiService.request1()
           firstChildJob = launch {
                apiService.request2()
                ........
           }
           secondChildJob = launch {
                apiService.request3()
                ........
           }
      }   
   }

   override fun onCleared(){
      parentJob?.cancel()
      firstChildJob?.cancel()
      secondChildJob?.cancel()
   }   
}

This above code works for our contrived example¬†but its hackiness makes me go ‚ÄúICK!‚ÄĚ. Currently, we’ve three¬†launch¬†and we have to keep track of three¬†Jobs¬†and, maybe later we need to have four¬†launch¬†and we need to keep track of four¬†Jobs. It gets little unwieldy and a bit too much code to actually maintain.¬†Trying to make this style work in any nontrivial program would be a complete nightmare.

To make the above example work in a conical way we need to have something like CompositeDisposable who can hold multiple other Disposable and dispose of them if any error occur or canceled them all with just one method. Happily! the kotlin coroutine library gives us the CoroutineScope.

Kotlin CoroutineScope

A CoroutineScope makes error handling easier, If any child coroutine fails, the entire scope fails and all of children coroutines are canceled. It should be implemented on entities with a well-defined lifecycle that is responsible for launching children coroutines. By entities, I mean Activity, Fragment, and ViewModel.

Every coroutine builder like launch and async are an extension of CoroutineScope and inherits corutineContext to automatically propagate both context elements and cancellation.

There are two ways to use to CoroutineScope in an application.

  • First, is to implement the¬†CoroutineScope on entities.
  • Second to use the coroutineScope top-level function which creates a new CoroutineScope and calls the specified¬†suspend¬†block with this scope.

1. Cancel Jobs By Implementing Kotlin CoroutineScope

Back to our previous parent-child coroutine example. Using CoroutineScope our example code looks like this:

class MyViewModel constructor(private val apiService : ApiService) : ViewModel(), CoroutineScope {   // 1
   // 2
   private val job = Job()
   // 3
   override val coroutineContext: CoroutineContext get() = job + Dispatchers.Main
   // 4
   fun executeCalls() {
      launch(context = coroutineContext) {
           val firstRequestDeferred = async {
                apiService.request1()
           }
           val secondRequestDeffered = async {
                apiService.request2()
           }
           handleResponse(firstRequestDeferred,await(),secondRequestDeffered.await())
      }   
   }
   // 5
   override fun onCleared(){
       job.cancel()
   }   
}

Below is the explanation of the above code:

  1. As I said, we need to implement CoroutineScope so that our launch and  async invoked in the scope of an entity.
  2. We created this Job instance so that we can easily cancel all async call whenever our ViewModel is going to destroy.
  3.  We need to tell in which dispatchers our response is needed to dispatch.
  4. Note that an implementation of launch also defines an appropriate coroutineContext. Now the parent coroutine waits for all the coroutines inside their block to complete before completing themselves. If the ViewModel is going to destroy or any of the launched coroutines throws an exception then all the nested coroutines are canceled.
  5. Cancel Job in ViewModel onCleared method. After destroying, all children jobs will be canceled automatically.

Note:¬†You see with the above approach we don’t need to keep track so many Jobs¬†if we cancel the parent coroutine or any Exception¬†thrown inside any of the coroutines then all jobs will be canceled.

2. Cancel Jobs With CoroutineScope Top-level Function

// 1
suspend fun executeCall(apiService : ApiService) = couroutineScope {
   val firstDeferredRequest = async {
         apiService.request1()
   }
   val secondDeferredRequest = async {
        apiService.request2()
   }
   handleResponse(firstRequestDeferred.await(),secondDeferredRequest.await())
}

// 2 
val job = launch {
    executeCall()
}

// 3
job.cancel()

With structured concurrency, you have to wrap your code into a coroutineScope block that establishes a boundary of your operation, its scope. All the async coroutines become the children of this scope and, if the scope fails with an exception or is canceled, all the children are canceled, too.

If you wanna know how the kotlin suspended function works under the hood, do check out this article => Explore How Kotlin Coroutine Works Under The Hood.

That is all for now!

If you like this article, I appreciate your support to share this article with others to let the great Android and Kotlin community know. I’m just a beginner like you guys. I write what I learned. Please let me know what did I miss in this article.

Thank you for being here and keep reading…

Almost in every Android mobile application, we have to deal with network calls and most of the time we end up using Retrofit. No doubt Retrofit is our favorite library when it comes to networking. Kotlin and now coroutine has made Networking on Android even easier than before.

I’ve recently listened to this talk by Florina Muntenescu. In this video, she talked about how Kotlin Couroutin Call Adapter made networking easy when working with Retrofit. In order to work with Kotlin Coroutine Call Adapter, there is a Call Adapter¬†created by Jake Wharton which uses Kotlin¬†Coroutine¬†Deferred¬†type.

What You’ll Learn In This Article

  1. We’ll see how to use sealed¬†class for Result both in a successful request and in error case.
  2. Add the Kotlin Coroutine Call Adapter when creating a Retrofit instance.
  3. We’ll see how to create a safe top-level function for every request which we made through Retrofit.
  4. Json arrives with its values and we’ll see how to parse the response.

In order to begin let’s add the following dependencies into our app level build.gradle file:

// ***** Networking Dependencies ***** //
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
    implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-experimental-adapter:1.0.0'

App Intro

I’ve built a Movie Searcher¬†application. In this app, we gonna simply hit an url and download some Json content and show them in a GridView. In order to get the response, we are going to use Retrofit and Kotlin Coroutine Call Adapter.

Kotlin Coroutine Call Adapter Retrofit Gif

Declaring Our Interface Or ApiService

For the above example, I used an API from TheMovieDb for demonstration. The following shows the ApiService interface.

interface ApiService {

    @GET(value = "popular")
    fun getPopular(
        @Query(
            value = "api_key"
        ) apiKey: String, @Query(
            value = "page"
        ) page: Int
    ): Deferred<Response<MovieResponse>>
}

You may have noticed that instead of Call<T>, we now have Deferred<T> defined in our interface function. Now our functions return a Deferred value rather than a Response.

The Deferred usually used with await suspending function in order to wait for the result without blocking the main/current thread.

Creating The Retrofit Service

In order to use Kotlin Coroutine Call Adapter, we need to add the instance of its factory when building our Retrofit ApiService.

companion object {
    private const val BASE_URL = "https://api.themoviedb.org/3/movie/"
}

fun getServiceApi(retrofit : Retrofit) = retrofit.create(ApiService::class.java)

fun getRetrofit() = Retrofit.Builder()
            .addCallAdapterFactory(CoroutineCallAdapterFactory())
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl(BASE_URL)
            .client(okHttpClient)
            .build()

val apiService = getServiceApi(getRetrofit())

Now we have Retrofit service instance with our Kotlin Coroutine Call Adapter Factory.

Sealed Classes For Result

If you guys have remembered, at the start of this article I said we’re gonna see how to use sealed classes to wrap-up the response in Sucess and in the Error case. You see the fact that we got the data either with Sucess or with Error and the way to do this is with sealed classes.

sealed class Result<out T: Any> {
    data class Success<out T : Any>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
}

Executing The Network Request

So for the implementation, we have MovieDataSource that would depend on the ApiService.

class MovieDataSource constructor(private val apiService: ApiService) {

     suspend fun getMovies(pageNumber : Int) : Result<MovieResponse>{
        val response = apiService.getPopular(API_KEY, pageNumber).await()
        if (response.isSuccessful)
            return Result.Success(response.body())
        return Result.Error(IOException("Error occurred during fetching movies!"))
     }

}

In order to fetch movies we would have a suspension function that would get us a parameter a pageNumber for the popular movies and with that, we return a Result of MovieResponse. In the getMovie method, we trigger a request to the back-end and wait for the response and in the end, we handle Success or Failure of the response.

If you wanna know how the kotlin suspended function works under the hood, do check out this article.

I know I know many of you saying what if an error occurred during network request. Yeah! we need to wrap this request inside a try/catch block.

class MovieDataSource constructor(private val apiService: ApiService) {

     suspend fun getMovies(pageNumber : Int) : Result<MovieResponse>{
        val response = apiService.getPopular(API_KEY, pageNumber).await()
        try {
             if (response.isSuccessful)
                   return Result.Success(response.body())
             return Result.Error(IOException("Error occurred during fetching movies!"))
        } catch(e : Exception){
            Result.Error(IOException("Unable to fetch movies!"))
        }
     }

}

By using the try/catch it looks like we handled the error case but for now every Network request we make we had to add this try/catch. Obviously, we did not want to do that.

Creating A SafeApiCall Top-level Function

For the sake of adding the try/catch to every Network request, we create a safeApiCall top-level function to just trigger the request.

suspend fun <T : Any> safeApiCall(call: suspend () -> Result<T>, errorMessage: String): Result<T> = try {
    call.invoke()
} catch (e: Exception) {
    Result.Error(IOException(errorMessage, e))
}

You see the safeApiCall is just a suspending function and most of all it also gets a suspending lambda as a parameter. So, inside this function, we just call the lambda, then in case, an error is thrown we just return the result of error based on the message that we pass as a parameter.

Update MovieDataSource Class

After creating a top-level suspending function we need to trigger our network request through the extension function.

class MovieDataSource constructor(private val apiService: ApiService) {

    suspend fun getMovies(pageNumber: Int) = safeApiCall(
        call = { popularMovies(pageNumber) },
        errorMessage = "Error occurred"
    )

    private suspend fun popularMovies(pageNumber: Int): Result<MovieResponse> {
        val response = apiService.getPopular(API_KEY, pageNumber).await()
        if (response.isSuccessful)
            return Result.Success(response.body()!!)
        return Result.Error(IOException("Error occurred during fetching movies!"))
    }
}

Now in our getMovies,¬†we’ll just call safeApiCall¬†and then we put our popularMovies method as a call and then we define an Error message.

By using the above approach we can focus on what matters when creating the network request and handling the response.

Interaction With The Suspending Function

So for the interaction with suspending function, we’re gonna work LiveData and ViewModel.

class MovieViewModel constructor(private val movieDataSource: MovieDataSource) : ViewModel() {

     // 1
     private val _movies = NonNullMediatorLiveData<List<MovieResponse.Movie>>()
     private val _error = NonNullMediatorLiveData<String>
     
     // 2
     val movies: LiveData<List<MovieResponse.Movie>> get() = _movies
     val erros : LiveData<String> get() = _error

     init {
        initGetMoviesCall()
     }
         
     // 3
     private fun initGetMoviesCall() {
        moviesJob = launch {
            val result = movieDataSource.getMovies(1)
            when (result) {
                is Result.Success -> _movies.postValue(value.data.movies)
                is Result.Error -> _error.postValue(value.exception.message)
            }
        }
     }      
}

Below is the explanation of the above code.

  1. I’m using NonNullMediatorLiveData class for _movies.¬†The¬†NonNullMediatorLiveData¬†extends from¬†MediatorLiveData¬†class. The purpose of it extending from¬†MediatorLiveData¬†to make a lot easier to use, especially making it¬†NonNull¬†safe. The same thing goes for an error case.
  2. It is only for not just exposing our MediatorLiveData publically, we’ve given a public function to just get _movies and _errors. By doing this we’re keeping our immutability principle.
  3. In order to execute a suspending¬†method, we need to trigger the request inside suspend block and launch is a suspending block. The launch launches the coroutine without blocking the current thread and returns a reference to the coroutine as a Job. Inside the launch block when the response came, we simply check if the result is Sucess then add data to movies and if it is an error then post the error message to _errors. Another thing to point out here is that you see we’re using the postValue method to set data, that’s because we’re inside the background thread context.

You can read more about how to work with LiveData and ViewModel together in this article.

MovieViewModelFactory

I believe you guys know that ViewModel by default doesn’t have any arguments that’s why we had to implement our own ViewModelFactory.

class MovieViewModelFactory constructor(private val movieDataSource: MovieDataSource) :
    ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>) = MovieViewModel(movieDataSource) as T
}

Cancel The Network Request

At some point, you may need to cancel the running async request because a user may rotate the device during the network request or maybe changing the current Activity. That’s why Job cancellation is always necessary!

override fun onCleared() {
    super.onCleared()
    moviesJob?.cancel()
}

// OR

fun cancelNetworkRequest() = moviesJob?.cancel()
 

NonNullMediatorLiveData

class NonNullMediatorLiveData<T> : MediatorLiveData<T>()

Interacting With UI

At this point, we have fully defined our backend API, ViewModel, and triggered a network request. Now we just need to listen to our movies or any error if they occurred.

private val movies = mutableListOf<MovieResponse.Movie>()

viewModel.movies
         .nonNull()
         .observe(this) {                
             this.movies.addAll(it)
             movieAdapter.notifyDataSetChanged()
         }

viewModel.error
         .nonNull()
         .observe(this) {
            showToast(it)
         }

You see in UI we’re simply observing our new movies. The UI does not need to know that, the movies are coming from the¬†MockRepo¬†or they came from the Network. It only knows how to show them.

Quick Note: The UI only needs to know how to show data not how to fetch it and perform business logic on it. All these things are kept under the hood.

Alright, guys, this was my demonstration on how we can improve our app network architecture when working with Kotlin Coroutine Call Adapter and Retrofit. If you guys want the source code of above app you can get it from GitHub.

If you’ve any advice on how to make our Networking Architecture better or how to extend the above example functionalities do comment below and discuss it.¬†ūüôÉ

In the ūüĒ• hype of async¬†programming, everyone is talking about Kotlin Coroutine. I know there are so much great articles on how to work with kotlin coroutines that’s why we gonna dive deep and see how the coroutines work under the hood.¬†Kotlin Coroutine¬†is a great and powerful language feature that’s why I think intro¬†on the coroutines is necessary.

Brief Intro On Kotlin Coroutine

Kotlin coroutine is a way of doing things asynchronously in a sequential manner. Creating a coroutine is a lot cheaper vs creating a thread.

“In Kotlin Coroutine Structure Of [Imperative], Synchronous Code is the same as Asynchronous Code.”¬† By Venkat Subramariam

¬† ¬†In order to do asynchronous programming, you may have had to use Tasks, Threads, Executors, Callbacks or even RxJava. Although RxJava has a large number of combinators, various operators, operators chaining but the downside of this you’ve to have a quite steep learning curve to understand the way asynchronous work.

Kotlin coroutine is one the “biggest feature ” as indicated by the following quote taken from JetBrain’s¬†blog.

We all know that blocking is bad under a high load, that polling is a no-go, and the world is becoming more and more push-based and asynchronous. Many languages (starting with C# in 2012) support asynchronous programming through dedicated language constructs such as async/await keywords. In Kotlin, we generalized this concept so that libraries can define their versions of such constructs, and async is not a keyword, but merely a function.
This design allows the integration of different asynchronous APIs: futures/promises, callback-passing, etc. It is also general enough to express lazy generators (yield) and cover some other use cases.

Adding Coroutine In Project

Kotlin coroutine in a project is very simple and also you need to enable it in your project. Since they are still an experimental mode, you may need to also include a few extra steps in your application level build.gralde file.

To begin to add the following library into your build.gradle file.

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.26.1'
}

You’ll also need to add the following lines to your gradle file.

kotlin {
    experimental {
        coroutines "enable"
    }
}

Now if you’re working with pom add the dependency in your pom.xml file.

<dependencies>
          <dependency>
            <groupId>org.jetbrains.kotlinx</groupId>
            <artifactId>kotlinx-coroutines-core</artifactId>
            <version>0.23.4</version>
        </dependency>
............
<dependencies/>

Also, add the configuration for the experimental mode in your pom.xml file

<build>
    <plugin>
         <configuration>
              <experimentalCoroutines>enable</experimentalCoroutines>
         <configuration/>
    .........
    <plugin/> 
.......
<build/>

Kotlin Coroutine Example

Coroutines have been around in programming language for a very long time. Kotlin is the not the first language to it definitely. Kotlin has a very hygienic syntax so it becomes really easy to engage that in the code and to be able to process that. Usually, when you’re writing a method you can mark a method as a¬†suspend¬†that would execute potentially in a different thread but more important asynchronously.

Let’s take a look at how this gonna look like with a little example where we can just kind of entertain the start and see how this would work.

// 1
val sequence = buildSequence {
    println("One")
    yield(1)

    println("Two")
    yield(2)

    println("Three")
    yield(3)
}

// 2
private fun printSequence() = sequence.forEach {
    println(it)
}

// 3
fun main(args: Array<String>) {
    printSequence()
}

// The output of above program

One
1
Two
2
Three
3

The following explains the above code.

  1. The buildSequence¬†is a wrapper API provided in Kotlin. It’s gonna build a sequence lazily yielding one by one.
  2. Method for to print the sequence values.
  3. The main method where we called the printSequence function.

Explaining The Output Of Above Program

You see when you execute this particular sequence of calls notice the output of the sequence how it goes back and forth between the program. So, we entered in the sequence printed the¬†one¬†at the top then the¬†1¬†then¬†Two¬†again the number which is¬†2¬†and so on and so forth. Notice that when we’re looping through after the first iteration of the¬†sequence we step right at the line number 5¬†and then execute till line 7.¬†After the buildSequence¬†yield 2, we immediately jumped right into the second iteration of a for-each loop. So this is really where you are jumping into the middle of a function and executing a part of it.

It’s intriguing to think about how in the world this possibly work. In the way this actually works is when you’re leaving off with¬†yield¬†like I mentioned earlier you’re not only returning this result but you’re taking the remaining part of the code and moving it at as with the Continuation.

What Is Continuation

Well, we cannot talk about coroutines without talking about Continuation, so under the hood, it really is a Continuation that plays a very important role. Imagine you make a call to a function but when you returned from the function what if you can return not just the result of the function but a context of where you returned. Now when the caller wants to continue back with you they can call it back into the context and continue further.

Explaining The Continuation Of Above Program Output

Before to start explaining above program output with Continuation I want to show you the bytecode. The following shows the bytecode of the above program.

Kotlin Coroutine Continuation

If you see the highlighted part in the picture you’ve noticed that there’s an extra parameter of Continuation attached with the sequence. The Continuation as a parameter all that is done for you automatically behind the scene. So, think of it as a lambda as two parts of a caller, one is the result of the caller but the other is the lambda. It means caller can use the result and then fire back right into the lambda and then you can continue executing where you left off.

Continuation plays a really vital role when it comes to using coroutines and you can dissect the code and see how this is working internally.

Continuation With Suspend Keyword

Let’s try to do something a little different here and see how we can put this is into a bit of practical situation. Now almost in every application, we need to make some network calls and read the result from some API service. Let’s say we have this API call where the user executes a request and read the current time of the server.

Instead of directly using the coroutine with the suspend method I wanna show you the example first with sequential then we gonna see the same example with suspend. Below is a sample code of sequential code.

fun getServerTime(): String {
    return URL("https://server_time.org/")
            .readText()
}

fun main(args: Array<String>) {
    launch { println(getServerTime()) }
}

The getServerTime¬†is very simple method it asks the server to give the current time. To clear things between suspend¬†and ¬†sequential¬†I’m gonna show you the first sequential bytecode of the above program.

sequential_code_bytecodeYou see with the arrow sign it is a simple method also in bytecode. Now let’s see the same example with a suspend keyword. The following shows code with suspend keyword.

suspend fun getServerTime(): String {
    return URL("https://server_time.org/")
            .readText()
}

fun main(args: Array<String>) {
    launch { println(getServerTime()) }
}

Bytecode of the above program.

concurreny_bytecodeYou see in the bytecode of getServerTime¬†function there’s a new parameter added of Continuation. To know this is suspended method it automatically adds a new parameter for you behind the scene. As a result of this when you execute this call you’re going to make rest of the code wrap it into a Kotlin¬†Continuation. So, that is an example of how a call can execute in a thread independent of this and you don’t have wait and block on it.

That is all for now!

Thank you for giving this article a read. I hope this article gives you the good understanding of how Kotlin Coroutine works. If you like it share it with Android Community. Let’s share the knowledge as much as we can.

Thank you for being here and keep reading…