This blog covers Kotlin Coroutines and suspend function with retrofit.
Coroutines ->
Coroutines can be used on Android to simplify async code. Android Jetpack APIs now have first class support for Kotlin Coroutines. Now, Kotlin 1.3 supports coroutines.
Coroutines are helpful in two main problems ->
- Long running task which can block main thread
- Main-safety allows you to ensure that any suspend function can be called from the main thread
Coroutines build upon regular functions by adding two new operations. In addition to invoke (or call) and return, coroutines add suspend and resume.
- suspend — pause the execution of the current coroutine, saving all local variables
- resume — continue a suspended coroutine from the place it was paused
Suspend Function ->
A suspending function is just a regular Kotlin function with an additional suspend modifier which indicates that the function can suspend the execution of a coroutine.
1 |
suspend fun getDataFromServer() |
You can only call suspend functions from other suspend functions, or by using a coroutine builder like launch
to start a new coroutine.
We use call back functions when we get response from our Async task. Suspend and resume work together to replace callbacks.
To understand suspend functions, we should also know about provided dispatchers by Kotlin.
To specify where the coroutines should run, Kotlin provides three dispatchers that you can use:
- Dispatchers.Main – Use this dispatcher to run a coroutine on the main Android thread. This should be used only for interacting with the UI and performing quick work. Examples include calling
suspend
functions, running Android UI framework operations, and updating LiveData objects. - Dispatchers.IO – This dispatcher is optimized to perform disk or network I/O outside of the main thread. Examples include using the Room component, reading from or writing to files, and running any network operations.
- Dispatchers.Default – This dispatcher is optimized to perform CPU-intensive work outside of the main thread. Example use cases include sorting a list and parsing JSON.
Lets, see this with an example -> We are calling our api through coroutines. So, we use Dispatchers.IO.
1 2 3 4 5 6 7 8 |
suspend fun getDataFromServer() { // Dispatchers.Main var result = callApi() // Dispatchers.IO for `callApi` showData(result) // Dispatchers.Main } suspend fun getDataFromServer() = withContext(Dispatchers.IO) { Apifactory.tmApiInterface.getData() } |
When we call callApi() suspend method, then it suspends our coroutine. The coroutine on the main thread will be resumed with the result as soon as the withContext block is complete.
Note: Using suspend doesn’t tell Kotlin to run a function on a background thread. It’s normal for suspend functions to operate on the main thread.
Use CoroutineScope with Android Architecture components ->
You can associate CoroutineScope implementations with a component lifecycle. This lets you avoid leaking memory or doing extra work for activities or fragments that are no longer relevant to the user. Using Jetpack components, they fit naturally in a ViewModel. Because a ViewModel isn’t destroyed during configuration changes (such as screen rotation). So, you don’t have to worry about your coroutines getting cancelled or restarted.
You can start coroutines in one of two ways -> launch & async
Some Architecture components, including ViewModel and LifeCycle , include built-in support for coroutines through their own CoroutinesScope members.
For example, ViewModel includes a built-in viewModelScope. This provides a standard way to launch coroutines within the scope of the ViewModel, as shown in the following example:
Kotlin Coroutines and suspend function with retrofit ->
We will cover this blog with launch example.
Now, lets see the example of Kotlin Coroutines and suspend function with retrofit.
In the below example, AndroidX is migrated.
To learn about androidX, you can refer this link -> AndroidX
If you want to migrate your project with AndroidX, then simply follow these steps ->
Android Studio(Android Studio 3.2 and higher) -> Refactor -> Migrate To AndroidX
Required Dependencies ->
1 2 3 4 5 |
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-beta01" //Kotlin Coroutines implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1" |
Activity ->
1 2 3 4 5 6 7 8 9 10 |
private lateinit var mainViewModel : MainViewModel private lateinit var binding : ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mainViewModel = ViewModelProviders.of(this).get(MainViewModel::class.java) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.data = mainViewModel } |
ViewModel Class ->
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class MainViewModel : ViewModel() { var listCustomer = MutableLiveData<List<Any>>() init { callApiMethod() } fun callApiMethod() { viewModelScope.launch { // Dispatchers.Main var resultDef : Deferred<Response<CompanyCustomerDataResponse>> = getDataFromServer() try { var result : Response<CompanyCustomerDataResponse> = resultDef.await() if (result.isSuccessful) { var resposne = result.body() resposne?.let { var list = resposne.results listCustomer.value = list } } else { // response not get } } catch (ex : Exception) { resultDef.getCompletionExceptionOrNull()?.let { println(resultDef.getCompletionExceptionOrNull()!!.message) } } } } /** * Heavy operation that cannot be done in the Main Thread */ suspend fun getDataFromServer() = withContext(Dispatchers.IO) { Apifactory.retrofitApiInterface.getCustomerData() } } |
Retrofit Api Interface ->
1 2 3 4 5 6 |
interface RetrofitApiInterface { @GET fun getCustomerData("getCustomerData"): Deferred<Response<CompanyCustomerDataResponse>> } |
Retrofit class ->
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
object Apifactory{ private val myOkHttpClient = OkHttpClient().newBuilder() .addInterceptor(getHttpLoggingInterceptor()) .build() fun retrofit() : Retrofit = Retrofit.Builder() .client(myOkHttpClient) .baseUrl(BASE_URL) // YOUR BASE URL OF API .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build() val tmdbApi : RetrofitApiInterface = retrofit().create(RetrofitApiInterface::class.java) private fun getHttpLoggingInterceptor(): Interceptor { val loggingInterceptor = HttpLoggingInterceptor() loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY return loggingInterceptor } } |
To learn more about kotlin & Coroutines, you can also refer this link -> Coroutines Guide
That’s done. Hope, this blog will be helpful to you.