dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
//Android architecture components - Kotlin version
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-rc03'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-rc03'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-rc03'
//Kotlin extensions for activity and fragments
implementation 'androidx.fragment:fragment-ktx:1.1.0'
implementation "androidx.activity:activity-ktx:1.0.0"
//Retrofit and OkHttp dependencies
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
implementation 'com.squareup.okhttp3:logging-interceptor:4.2.2'
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
implementation 'com.squareup.retrofit2:converter-scalars:2.6.2'
implementation 'com.google.code.gson:gson:2.8.6'
}
Kotlin Coroutines with Retrofit
Upasana | August 01, 2020 | 4 min read | 2,667 views
In this tutorial we will use android architecture components for making Kotlin coroutine enabled REST API calls using Retrofit in Android Application.
Gradle setup
We will be adding the following dependencies to our build.gradle
- android architecture components, kotlin extensions for activity and fragments, and retrofit + okhttp
Creating Retrofit API Client
First of all we need to create an instance of OkHttpClient and Retrofit client that we will use later on for creating instance of Services.
object ApiClient {
private val okHttpClient by lazy { OkHttpClient() }
private val retrofit: Retrofit by lazy {
Log.e("AppClient", "Creating Retrofit Client")
val builder = Retrofit.Builder()
.baseUrl("https://reqres.in")
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
val dispatcher = Dispatcher()
dispatcher.maxRequests = 1
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val client: OkHttpClient = okHttpClient.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.dispatcher(dispatcher)
.build()
builder.client(client).build()
}
fun <T> createService(tClass: Class<T>?): T {
return retrofit.create(tClass)
}
}
Creating Service Interface
For this tutorial, we will be using a sample hosted service at https://reqres.in that returns person details.
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import retrofit2.Response
import retrofit2.http.GET
/**
* @author Munish Chandel
*/
interface EmployeeApi {
@GET("/api/users/2")
suspend fun getPerson(): Response<ServiceResponse<Person>> (1)
}
@Keep
data class ServiceResponse<T>(
@SerializedName("data") val data: T
)
@Keep
data class Person(
@SerializedName("id") val id: Long,
@SerializedName("email") val email: String,
@SerializedName("first_name") val firstName: String,
@SerializedName("last_name") val lastName: String
)
1 | suspend function allows us to run this method inside a coroutine. suspend variant is only available since retrofit 2.6+ |
Generic data class for holding network responses
We need a class that can hold data as well as network status like loading, success and error. The following implementation shall be good enough to start with.
data class Resource<out T>(val status: Status,
val data: T?,
val msg: String?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(msg: String, data: T? = null): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> loading(data: T? = null): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
enum class Status {
SUCCESS,
ERROR,
LOADING
}
Creating ViewModel for interacting with Service
ViewModel will interact with the service and fetches the data from network using Kotlin coroutine tied to viewModelScope. So the request will be automatically cancelled if fragment is destroyed.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import kotlinx.coroutines.Dispatchers
class MainViewModel : ViewModel() {
fun loadData() = liveData(Dispatchers.IO) { (1)
emit(Resource.loading())
val api = ApiClient.createService(EmployeeApi::class.java)
val response = api.getPerson()
if(response.isSuccessful) {
emit(Resource.success(response.body()?.data))
}
}
}
1 | liveData is handy kotlin extension function that enables us to create and run a Kotlin coroutine tied to viewModelScope. |
Fragment using ViewModel for communication
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import com.shunya.myapplication.R
import kotlinx.android.synthetic.main.main_fragment.*
class MainFragment : Fragment() {
companion object {
fun newInstance() = MainFragment()
}
private val viewModel: MainViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.main_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel.loadData().observe(this, Observer { networkResource ->
when (networkResource.status) {
Status.LOADING -> {
message.text = "loading data from network"
}
Status.SUCCESS -> {
val person = networkResource.data
person?.let {
message.text =
person.firstName + " " + person.lastName + "\n" + person.email
}
}
Status.ERROR -> {
message.text = "error loading data from network"
}
}
})
}
}
Grab the source code
https://github.com/cancerian0684/tutorial-android-retrofit-kotlin
references
-
https://reqres.in for making sample REST calls
Top articles in this category:
- Retrofit Basic Authentication in Android
- Retrofit OAuth2 Bearer Token Authentication OkHttp Android
- Service vs Intent Service in Android
- Firebase Cloud Messaging in Android App using Command Pattern
- iOS interview experience fresher
- iOS interview questions for 0-3 years experience
- FirebaseInstanceIdService is deprecated now