Android LiveData on Kotlin using Retrofit and coroutines

  • Tutorial
This article is about using Android Components ViewModel, LifeCycle and LiveData. These components allow you to not care about the life cycle of the Activity.

Also considered an example of the use of modern Coroutines in conjunction with the repository on the Retrofit.

funmain(args: Array<String>): Unit = runBlocking {
    // Wait (suspend) for Resultval result: Result<User> = api.getUser("username").awaitResult()
    // Check result typewhen (result) {
        //Successful HTTP resultis Result.Ok -> saveToDb(result.value)
        // Any HTTP erroris Result.Error -> log("HTTP error with code ${result.error.code()}", result.error)
        // Exception while request invocationis Result.Exception -> log("Something broken", e)

Retrofit coroutines extension

Extension for Retrofit on Kotlin. These are just two files. I just added them to the project. You can connect them through Dependency in Gradle. There are usage examples on Github.
Also connect Adapter addCallAdapterFactory (CoroutineCallAdapterFactory ()) .
ServerAPI and Repository are in the same

REST API file by

implementing the REST API on Kotlin. It does not have any specific changes.

import android.arch.lifecycle.MutableLiveData
import android.util.Log
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.experimental.CoroutineCallAdapterFactory
import kotlinx.coroutines.experimental.async
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Call
import retrofit2.http.*
import ru.gildor.coroutines.retrofit.Result
import ru.gildor.coroutines.retrofit.awaitResult
object ServerAPI {
    var API_BASE_URL: String = getNetworkHost();
    var httpClient = OkHttpClient.Builder().addInterceptor(
        HttpLoggingInterceptor().apply {
            level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
    var builder: Retrofit.Builder = Retrofit.Builder()
    var retrofit = builder
    var netService = retrofit.create<NetService>(!!)interfaceNetService{
        @GET("api/stores")fungetStoreAll(@Header("Authorization") bearer: String): Call<Array<Store>>


Next, consider the Repository. This is the main service for getting livedata. Initialize LiveData with the load state: Resource.loading (null) . Next, we expect the end of the request. AwaitResult () This call should be in the Coroutin async (UI) block.

At the end of the request, we can handle the result. If all is well, the result will be saved in mutableLiveData.value = Resource.success (result.value) The important point is that the link to the new instance should be, otherwise observer LiveData will not work. see: new Resource <> (SUCCESS, data, null);

    fungetStores(token: String)  :  MutableLiveData<Resource<Array<Store>>>{
        val mutableLiveData = MutableLiveData<Resource<Array<Store>>>()
        mutableLiveData.value = Resource.loading(null)
        val req = PostsAPI.netService.getStoreAll(token)
        try {
            async(UI) {
                val result = req.awaitResult()
                // Check result typewhen (result) {
                    //Successful HTTP resultis Result.Ok -> {
                        mutableLiveData.value = Resource.success(result.value)
                    // Any HTTP erroris Result.Error -> {
                        mutableLiveData.value  = Resource.error("Http Error!", null)
                    // Exception while request invocationis Result.Exception -> Log.d(TAG, result.exception.message)
        } catch (e: Exception) {
            Log.d(TAG, e.toString())
        return mutableLiveData

Wrapper data

Wrapper - Resource <T> is used to handle errors and transfer status to the Fragment .

It store three states:

publicenumStatus{ SUCCESS, ERROR, LOADING }

The data itself:

@Nullablepublicfinal T data;

Resource <T>
A generic class that contains data and status about loading this data

// A generic class that contains data and status about loading this data.publicclassResource<T> {
    @NonNullpublicfinal Status status;
    @Nullablepublicfinal T data;
    @Nullablepublicfinal String message;
    privateResource(@NonNull Status status, @Nullable T data,
            @Nullable String message){
        this.status = status; = data;
        this.message = message;
    publicstatic <T> Resource<T> success(@NonNull T data){
        returnnew Resource<>(Status.SUCCESS, data, null);
    publicstatic <T> Resource<T> error(String msg, @Nullable T data){
        returnnew Resource<>(Status.ERROR, data, msg);
    publicstatic <T> Resource<T> loading(@Nullable T data){
        returnnew Resource<>(Status.LOADING, data, null);
    publicenum Status { SUCCESS, ERROR, LOADING }


StoresViewModel requests data from the repository and stores in the internal variable stores

val api = Repository()
stores = api.getStores(token)

classStoresViewModel (context: Context, token: String) : ViewModel() {
    val stores: MutableLiveData<Resource<Array<Store>>>
    init {
        val api = Repository()
        stores = api.getStores(token)


To transfer parameters to the ViewModel, we will extend the standard ViewModelProviders
For example, two parameters (Login, Password) are required to transfer to the LoginViewModel . To send a token to StoresViewModel, use one (Token)

classAppViewModelFactory(privateval contect: Context, vararg params: Any) :
    ViewModelProvider.NewInstanceFactory() {
    privateval mParams: Array<out Any>
    init {
        mParams = params
    overridefun<T : ViewModel>create(modelClass: Class<T>): T {
        returnif (modelClass == {
            LoginViewModel(contect, mParams[0] as String, mParams[1] as String) as T
        } elseif (modelClass == {
            StoresViewModel(contect, mParams[0] as String) as T
        } else {


Getting StoresViewModel:

viewModel = ViewModelProviders.of(this, AppViewModelFactory(requireActivity(), tokenHolder.token)).get(

Using Observer for data changes:

// Observe data on the ViewModel, exposed as a LiveData
 viewModel.stores.observe(this, Observer<Resource<Array<Store>>> { storesResource ->

        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.stores_fragment, container, false)
        val tokenHolder = TokenHolder(PreferenceManager.getDefaultSharedPreferences(requireActivity()))
        viewModel = ViewModelProviders.of(this, AppViewModelFactory(requireActivity(), tokenHolder.token)).get(
        recyclerView = view.findViewById<RecyclerView>( {
        return view
    overridefunonActivityCreated(savedInstanceState: Bundle?) {
        // Observe data on the ViewModel, exposed as a LiveData
        viewModel.stores.observe(this, Observer<Resource<Array<Store>>> { storesResource ->
            val stores = storesResource?.data
            stores?.let {
                viewAdapter = StoresAdapter(stores!!)
                recyclerView.adapter = viewAdapter
            if (storesResource?.status  == Resource.LOADING){
            if (storesResource?.status  == Resource.ERROR){
                log("Error : " + storesResource?.message)


To store the Token and use it throughout the application, I applied the library / extension from Fabio Collini. The application is well described in his article. The link is on the page in Github or lower in this article.

prefs-delegates by Fabio Collini

classTokenHolder(prefs: SharedPreferences) {
    var token by prefs.string()
        privatesetvar count by
        privatesetfunsaveToken(newToken: String) {
        token = newToken


    implementation 'android.arch.lifecycle:extensions:1.1.1'
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.30.0"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.30.0"
    implementation "com.squareup.retrofit2:retrofit:2.4.0"
    implementation "com.squareup.retrofit2:converter-gson:2.4.0"
    implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-experimental-adapter:1.0.0"
    // If you use Kotlin 1.2or1.3
    // compile 'ru.gildor.coroutines:kotlin-coroutines-retrofit:0.13.0'
    // compile 'ru.gildor.coroutines:kotlin-coroutines-retrofit:0.13.0-eap13'


All in one example

Android Architecture Components samples

LiveData Overview

Async code using Kotlin Coroutines

Multithreading and Kotlin

Only registered users can participate in the survey. Sign in , please.

What development tools and programming language for Android do you use?

Also popular now: