Network Calls with Retrofit – Kotlin Tutorial Series – 4

Hello everyone,

In this tutorial, I am going to make network calls from an android application via retrofit in MVVM pattern. End of the article, I am going to share the tutorial’s GitHub link.

For sample requests, I am going to use apis from https://jsonplaceholder.typicode.com/

Before starting the main point of this article, I would like to explain something about the MVVM pattern. MVVM is one of the most popular application development architecture in the developer’s world. It is useful because it separates the layers. In this architecture, we have a three layer. They are Model, View and ViewModel. Let’s quickly look at what those layers are including.

Model layer holds data and validates it. It can be any data source (local/remote) or any model classes. View layer is the application’s XML screen designs. Like an activity or fragment. It is responsible for sending user actions to the ViewModel layer. ViewModel layer is responsible for UI related data actions. Such as database crud operations or any data manipulation. With the tutorial, hopefully it is going to be more clear.

After a short information break, time to focus on the main subject. Like I said, I am going to create a network call in this tutorial and for this I need to include retrofit dependencies. What is Retrofit? Retrofit is a REST client for android and java. It is type-safe and It makes it easier to do network calls from an android application. To include this library to your current project you have to add the following dependency to your project.

implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2"

Retrofit is okay but I am going to add one more dependency. It’s Moshi and It is going to convert the JSON result to my model object. For this converting operation, it is possible to find more alternatives but Moshi is one of the best and I prefer it in my applications. To add Moshi to my application, I am going to add the following dependencies.

implementation "com.squareup.moshi:moshi:1.9.3"
implementation "com.squareup.moshi:moshi-kotlin:1.9.3"

After adding those dependencies and sync to the project, it is ready for development.

First of all I am going to create a new package and create a RetrofitService.kt file inside of this package. The screenshot for this step is like the left hand side.

Inside of the RetrofitService.kt file, I am going to create a variable for Moshi because I have to use this variable in my Retrofit definition. My variable for Moshi is like the following.

private val moshi = Moshi.Builder()
        .add(KotlinJsonAdapterFactory())
        .build()

In this definition, I just added an adapter for Kotlin and completed to create my variable. Now, I am going to do a similar thing to create a Retrofit variable. Codes for the retrofit variable is like the following.

private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .baseUrl(BASE_URL)
        .build()

I just forgot the add Base URL. Top of the RetrofitService.kt file, I am going to add a base URL.

private const val BASE_URL = "https://jsonplaceholder.typicode.com/"

Retrofit is nearly ready to use. Now, I am going to create an interface in the same file (RetrofitService.kt) What will include this file? I am going to define api functions. Request parameters, response types, methods etc. 

In this interface, I am going to add one sample function. It is going to get all todo objects. Basically, my interface is going to be like the following. In this step, I used TodoModel object and I am going to define this in the next steps.

interface RetrofitSampleService {
    @GET("todos")
    suspend fun getAllTodos(): List<TodoModel>
}

I define my function with a suspend key because I am going to use coroutines. I have to add the last one code block to this RetrofitService.kt file. When I want to access api methods via retrofit, I am going to use the following object. The completed RetrofitService.kt file is like the following.

package com.example.retrofitarticlesample.api

import com.example.retrofitarticlesample.domain.TodoModel
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET

private const val BASE_URL = "https://jsonplaceholder.typicode.com/"

private val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()

private val retrofit = Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .addCallAdapterFactory(CoroutineCallAdapterFactory())
    .baseUrl(BASE_URL)
    .build()

interface RetrofitSampleService {
    @GET("todos")
    suspend fun getAllTodos(): List<TodoModel>
}

object RetrofitApi {
    val retrofitService: RetrofitSampleService by lazy {
        retrofit.create(RetrofitSampleService::class.java)
    }
}

End of the RetrofitService.kt file, I added a RetrofitApi object. I am going to use this object to access the retrofit service. With this, I completed the setup on the retrofit side. Now, I am going to create my model (TodoModel) like mentioned before and a viewModel file. TodoModel is basically like the following. I am going to use this model for incoming data from the retrofit service.

package com.example.retrofitarticlesample.domain

data class TodoModel(
    val userId: Int,
    val id: Int,
    val title: String,
    val completed: Boolean
)

Now, time to use this retrofit service. For this, firstly I am going to create a viewModel file. Like I said at the start of the article, I am going to do data operations in this class. CRUD operations or any data operation. It is going to connect retrofit api and It is going to bind my list object via getTodoList function. My basic viewModel object is like the following.

Note: In this viewModel file, I can not create any android os api object. Like TextToSpeech, Notification manager etc.

package com.example.retrofitarticlesample

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.retrofitarticlesample.api.RetrofitApi
import com.example.retrofitarticlesample.domain.TodoModel
import kotlinx.coroutines.launch

class MainActivityViewModel : ViewModel() {
    var todoList = MutableLiveData<List<TodoModel>>()

    fun getTodoList() {
        viewModelScope.launch {
            todoList.value = RetrofitApi.retrofitService.getAllTodos()
        }
    }
}

In the next step, to use viewModelScope.launch (coroutines) you have to add the following dependencies to your gradle file and sync your project.

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"

Nearly we are done. In the last step, I am going to use this viewModel file in my Activity. Basically the usage is like the following.

I defined my viewModel object in the activity and started to observe todoList objects. When I get the data via retrofit I just send a log message to the console.

package com.example.retrofitarticlesample

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MainActivityViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)

        viewModel.getTodoList()

        viewModel.todoList.observe(this, Observer {
            if(it.isNotEmpty()) {
                Log.i("information", "working")
            }
        })

        setContentView(R.layout.activity_main)
    }
}

Just before running the application, I have to make sure my application has the permission for the internet. If It does not, I have to add the following permission to AndroidManifest.xml file.

<uses-permission android:name="android.permission.INTERNET" />

After this, If I run the application, I can see the result in my Logcat.

Screenshot 2: The result.

GitHub: cinarrtolga/ART-Retrofit: This project created for Retrofit tutorial. (github.com)

I tried to explain network calls with retrofit for Android applications. If you see any mistake or wrong explanation in the article, you can contact with me via comment or article[at]cinarr[dot]com

See you 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *