First of all, what is Retrofit?
As its web site says is A type-safe HTTP client for Android and Java ( We are going to use Kotlin...). So, basically is a REST client for Android. It helps us to get and upload data throughout a REST API.
What about coroutines?
On Android, coroutines help to manage long-running tasks that might otherwise block the main thread and cause your app to become unresponsive.
Now, let's take a look at a basic implementation
First, we need to add some dependencies to our project. Starting with adding the version variables in our build.gradle(), under build script
buildscript {
ext {
version_moshi = "1.8.0"
version_retrofit = "2.5.0"
version_retrofit_coroutines_adapter = "0.9.2"
version_kotlin_coroutines = "1.1.0"
version_retrofit_coroutines_adapter = "0.9.2"
}
…
}
Then add the next dependencies to the build.gradle(:app)
//coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines"
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"
//Retrofit
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
//moshi
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
Build your project and now we are ready to start.
In this example, I'm going to use Advice Slip JSON API. More specific https://api.adviceslip.com/advice this URL returns a random advice every time you call it.
Here the JSON we get:
{
"slip": {
"id": 45,
"advice": "Build something out of LEGO."
}
}
So first we are going to create a new package named “network” in which we are going to add two Kotlin files:
- AdviceProperty.kt
- AdviceApiService.kt
In AdviceProperty.kt we are going to add the properties of the JSON in a data-class structure. Like the next example:
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
data class AdviceProperty(
val slip: AdviceData
): Parcelable
@Parcelize
data class AdviceData (
val id: Int,
val advice: String
): Parcelable
As you can see, we follow our JSON structure. Our first data-class has a property called slip whose type is AdviceData, this type is another data class with two properties id and advice. This is how we can replicate our nested JSON structure.
Then in AdviceApiService.kt we are going to set up our Retrofit instance.
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import kotlinx.coroutines.Deferred
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
// The base URL where our API is
private const val BASE_URL = "https://api.adviceslip.com/"
/* Moshi Makes it easy to parse JSON into objects
you can use GSON instead if you want*/
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
//Here is our retrofit instance
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
//Then we are going to create the interface
which is going to help to handle our GET method
to call the API*/
interface AdviceApiService{
@GET("advice")
fun getProperty():
Deferred<AdviceProperty>
}
/* Singleton to create this instance only once
and get it ready every time we call it. */
object AdviceApi {
val retrofitService : AdviceApiService by lazy {
retrofit.create(AdviceApiService::class.java)
}
}
Alright!, we're almost done!
I'm using the MVVM Architecture, which means for each fragment/activity I have a ViewModel Class in which I manage all the data for my app, and also this API call.
So in the ViewModel (this can be your class), I have a variable named _properties which is going to store my data, you can see its type it's AdviceProperty (Remember our class in the network package)
private var _properties = MutableLiveData<AdviceProperty>()
Next, I have other two variables:
private val viewModelJob = Job()
private val coroutineScope = CoroutineScope( viewModelJob + Dispatchers.Main )
The first one is the Job which is gonna define the lifecycle of the coroutine scope. So if its children fail, its gonna notify the parent and it will cancel the other children in case we have more instances running.
The second one is our coroutineScope, here we define in which thread our coroutine is going to run.
Then the method who is going to create the coroutine, and also manage the call to the API: getAdviceProperty
private fun getAdviceProperty(){
coroutineScope.launch {
var getProperty = AdviceApi.retrofitService.getProperty()
try {
_properties.value = getProperty.await()
}catch (e: Exception){
//library to show logs
Timber.i("EROR ${e}");
}
}
}
As you can see here, we have coroutineScope.launch this block of code allows us to create a new coroutine and run it in the thread we define previously.
Then we can see in the getProperty variable the word “await”, this is because our Singleton class returns a Deferred object (feature or a promise in java).
If you are using view models do not forget to cancel the Job in the onClear() method
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
And that’s it! We have now our response in a variable and we can use it wherever we want. It is much less code than we use to write with java or without coroutines.
Take a look at my repository: lucky Piñata It has a working example of this topic.
Top comments (0)