Android Retrofit with Coroutine demo

1. Add dependencies for the coroutine and retrofit in the gradle

// Coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'

// Retrofit 2 for network tasks
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
implementation 'com.google.code.gson:gson:2.8.2'

2. Create factory class for creating retrofit instance. RestUtil.kt

import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

class RestUtil private constructor() {
    private val API_BASE_URL = "https://api.github.com/"
    val retrofit: Retrofit

    init {
        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        val httpClient = OkHttpClient.Builder().addInterceptor(interceptor).build()

        val builder = Retrofit.Builder()
            .baseUrl(API_BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(CoroutineCallAdapterFactory())

        retrofit = builder.client(httpClient).build()
    }

    companion object {
        private var self: RestUtil? = null

        val instance: RestUtil
            get() {
                if (self == null) {
                    synchronized(RestUtil::class.java) {
                        if (self == null) {
                            self = RestUtil()
                        }
                    }
                }
                return self!!
            }
    }

}

3. Create data model class for holding the API result. GithubAccount.kt since we are using github api for getting github account in this demo.

import com.google.gson.annotations.SerializedName
data class GithubAccount(
        @SerializedName("login") var login : String = "",
        @SerializedName("id") var id : Int = 0,
        @SerializedName("avatar_url") var avatarUrl : String = "",
        @SerializedName("created_at") var createdAt : String = "",
        @SerializedName("updated_at") var updatedAt : String = "") {

    override fun equals(obj: Any?): Boolean {
        return login == (obj as GithubAccount).login
    }
}

4. Create Retrofit interface with coroutine for the REST service. Deferred makes it a coroutine result.

import kotlinx.coroutines.Deferred
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Path

interface GithubApi {
    @GET("/users/{username}")
    fun getGithubAccount(@Path("username") username: String): Deferred>
}

5. Finally, using coroutine and retrofit in the activity class.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val githubService = RestUtil.instance.retrofit.create(GithubApi::class.java)

        // launch a coroutine for doing the network task
        GlobalScope.launch(Dispatchers.Main) {
            // slowdown the process a bit to demonstrate everything in this coroutine is sequential
            // everything outside of this launch block is not blocked by this coroutine
            delay(5000)

            val response  = githubService.getGithubAccount("google").await()
            progress_circular.visibility = View.GONE
            if (response.isSuccessful) {
                if (response.body() != null) {
                    tv_content.text = "${response.body()!!.login} \n ${response.body()!!.createdAt}"
                }
            } else {
                Toast.makeText(applicationContext, "Error ${response.code()}", Toast.LENGTH_SHORT).show()
            }
        }

        Toast.makeText(applicationContext, "The above network call in coroutine is not blocking this toast", Toast.LENGTH_LONG).show()

    }

}

The coroutine part in the above should be in a ViewModel or repository class, they are here only because the purpose of this post is to demonstrate using coroutine with retrofit. The coroutine in the following code is everything in the launch block. Everything in this launch block are executed sequentially, each line is executed one after another, if one of the lines is taking it’s sweet time to do its things, the next line will have to wait until the previous line finishes. However, everything outside of this launch block won’t be waiting for the anything in the launch block(coroutine).

Coroutine is similar to spin up a thread and run it in the background, but coroutine is a lot more light-weight than thread, you can spin up millions of coroutine, and your program will still run fine, but a million of thread will likely make it run out of memory.

The above network task can also be done using Rxjava. With RxJava, we will have to make sure to subscribe the observable on an background thread, and observe the result on the main thread, it will involve some callbacks, and the code will not be sequential. With coroutine, everything in the coroutine are executed sequentially, no nested callbacks.

activity_main.xml




    

    


Complete example in Github

Search within Codexpedia

Custom Search

Search the entire web

Custom Search