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
Search within Codexpedia
Search the entire web