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