How to Use Ktor-Client in Your Android App

Abhishek Srivastava
4 min readOct 21, 2022

--

Ktor is an open-source framework for building asynchronous servers and clients in connected systems using the powerful Kotlin programming language. It runs on coroutines and was made by JetBrains.

Ktor can be compared to network library such as OkHttp and Retrofit.

Ktor is an asynchronous HTTP client that runs on several platforms. Ktor client is designed for various platforms, such as Android, Native (iOS and desktop), JVM, and JavaScript. Ktor is built on Kotlin multi-platform mobile (KMM). This means you can create both iOS and Android applications with Kotlin and share a huge part of Kotlin code for both platforms. It means that Kotlin multi-platform mobile uses Kotlin as the base code. With this, you have to use the Kotlin libraries if you want to share the code across Android and iOS. Ktor client is a Kotlin based library, thus making it easier to implement the KMM principles.

Why we use Ktor’s

As in android already have a very powerful Retrofit library so why we need it. So Answer is Retrofit library is platform-dependent and if we want to use for Multiplatform application like Android recently launch then how we can achieve from retrofit so now ktor’s comes in mind for that solution.

Ktor’s uses coroutines internally for asynchronous programming which makes our code very clean and minimal just like coroutines did with suspend functions. Moreover it is as easy to add headers, serialization, logging etc in our app, as it is with Retrofit.

Aim

This guide will help you learn more about Ktor. We will set up Ktor client to make HTTP requests to a JSON API and display the data using kotlin.

Setting up Android project for Ktor-Client

Adding the required libraries

Let’s add all the necessary libraries that we need to process and display data. We need the following libraries:

Ktor dependencies

// HTTP engine: that handles network requests.
implementation 'io.ktor:ktor-client-android:1.5.0'
// It used for JSON serialization and deserialization settings and //is recommended for multiplatform projects
implementation 'io.ktor:ktor-client-serialization:1.5.0'
//It is kotlinx.serialization, which is used for entity //serialization
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1'
//It's for logging HTTP requests
implementation 'io.ktor:ktor-client-logging-jvm:1.5.0'

you must add below line in the app.gradle file

plugins {
id 'kotlinx-serialization'
}

Also required changes in project.gradle

plugins {
id 'com.android.application' version '7.2.2' apply false
id 'com.android.library' version '7.2.2' apply false
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
// id 'kotlinx-serialization' version "0.4.1" apply true
// id 'kotlin-multiplatform' version '1.3.20'
id 'kotlinx-serialization' version '1.3.20'
}
configurations {
ktorDependecy
}

dependencies {

ktorDependecy 'org.jetbrains.kotlin:kotlin-serialization:1.7.10'
}

here ‘kotlin-multiplatform’ is required if you creating application for multiplatform otherwise you can use “kotlinx-serialization” and also classpath “org.jetbrains.kotlin:kotlin-serialization” required for serialization and deserialization purpose.

Now lets add maven “https://kotlin.bintray.com/kotlinx” in setting.gradle (if using gradle version 7.3.3 or later) and also need to add plugin. Here we have used both if multiple form required then we can “kotlin-multiplatform” otherwise for Android “kotlinx-serialization” will be enough.

pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
maven { url "https://kotlin.bintray.com/kotlinx" }
}
resolutionStrategy {
eachPlugin {
if (requested.id.id == "kotlin-multiplatform") {
useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${requested.version}")
}
if (requested.id.id == "kotlinx-serialization") {
useModule("org.jetbrains.kotlin:kotlin-serialization:${requested.version}")
}
}
}
}

Now create a HttpClient class for handle All type of request, but here we are only using Android client.

import android.util.Log
import io.ktor.client.*
import io.ktor.client.engine.android.*
import io.ktor.client.features.*
import io.ktor.client.features.json.*
import io.ktor.client.features.json.serializer.*
import io.ktor.client.features.logging.*
import io.ktor.client.features.observer.*
import io.ktor.client.request.*
import io.ktor.http.*

private const val TIME_OUT = 6000

private val httpClientAndroid= HttpClient(Android){

install(JsonFeature){

serializer= KotlinxSerializer(kotlinx.serialization.json.Json{
prettyPrint= true
isLenient= true
ignoreUnknownKeys= true
})

engine {
connectTimeout= TIME_OUT
socketTimeout= TIME_OUT
}
}

install(Logging) {
logger = object : Logger {
override fun log(message: String) {
Log.v("Logger Ktor =>", message)
}

}
level = LogLevel.ALL
}

install(ResponseObserver) {
onResponse { response ->
Log.d("HTTP status:", "${response.status.value}")
}
}

install(DefaultRequest) {
header(HttpHeaders.ContentType, ContentType.Application.Json)
}
}

Now lets create a model class to use any api

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class UserEntity(
@SerialName("age")
val age: Int,
@SerialName("count")
val count: Int,
@SerialName("name")
val name: String,)

After create model lets create a API class which will handle all request.

import io.ktor.client.*
import io.ktor.client.request.*

class UserApi(val client: HttpClient) {

val END_POINT_GET_USER_KTOR="https://api.agify.io/"
val SUB_END_POINT_GET_USER_KTOR="?name="
val END_POINT_POST_USER_KTOR=""

suspend fun getAgeOfPersonByName( userName: String):UserEntity=
client.get("$END_POINT_GET_USER_KTOR$SUB_END_POINT_GET_USER_KTOR$userName")

suspend fun addUser(user: UserEntity) {
client.post<UserEntity>(END_POINT_POST_USER_KTOR) {
body = user
}
}
}

Now we create a repository class where all required method’s will be mention.

interface UserRepository {
suspend fun getAgeOfPersonByName( name: String): Either<Failure, UserEntity>
}

Now create a repository implementation class which will handle all request and response.

class UserRepositoryImpl( private val networkHandler: NetworkHandler,
private val userApi: UserApi):UserRepository {
override suspend fun getAgeOfPersonByName(name: String): Either<Failure, UserModel> {
when (networkHandler.isConnected) {
true -> {
try {
Either.Right(userApi.getAgeOfPersonByName(name).toDomain())
} catch (e: Exception) {
Either.Left(e.toCustomExceptions())
}
}
else -> Either.Left(Failure.NetworkConnection)
}
}

Now in our project we can use this class to call api method and will get the success or fail response in our caller class.

For more detail, We will discuss more in the next part.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Abhishek Srivastava
Abhishek Srivastava

Written by Abhishek Srivastava

Senior Software Engineer | Android | Java | Kotlin | Xamarin Native Android | Flutter | Go

Responses (1)

Write a response