Networking with Ktor-Client — Android

Pankaj Rai 🇮🇳
3 min readNov 9, 2023

Ktor is an asynchronous, lightweight, and versatile framework for building web applications in Kotlin. While Ktor is often praised for its server capabilities, it also includes a powerful client library known as the Ktor Client. The Ktor Client allows developers to easily make HTTP requests, consume web services, and interact with APIs, all while leveraging the full potential of Kotlin’s expressive syntax. In this article, we will explore Ktor Client and learn how to harness its features for building efficient and responsive client-side applications.

What is Ktor Client?
Ktor Client is a part of the Ktor framework, which is designed to simplify the process of making HTTP requests in Kotlin-based applications. It provides an asynchronous and non-blocking way to interact with web services, making it a perfect choice for modern, highly concurrent applications. Whether you are developing a mobile app, web app, or backend service, Ktor Client can be a valuable tool in your Kotlin toolbox.

Adding Ktor-Client
Let’s add Ktor-Client to the native Android app, though the steps may differ a bit it will be quite similar even for the multiplatform application. First, let’s add the following Gradle dependencies

implementation("io.ktor:ktor-client-okhttp:2.3.5")
implementation("io.ktor:ktor-client-logging-jvm:2.3.5")
implementation("io.ktor:ktor-client-serialization:2.3.5")
implementation("io.ktor:ktor-client-content-negotiation:2.3.5")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.5")

Ktor network call works based on engine concept, we have different engines with respect to the platform and the one that we are going to use with our Android application is OkHttp.

Here is the list of all available engines

Even though the Android engine is available, OkHttp is a better choice because it supports HTTP/2.

Let’s create a HttpClient reference that we will use to perform network call

import io.ktor.client.HttpClient
import io.ktor.client.plugins.DefaultRequest
import io.ktor.client.plugins.HttpRequestRetry
import io.ktor.client.plugins.UserAgent
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.observer.ResponseObserver
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import io.ktor.util.appendIfNameAbsent
import kotlinx.serialization.json.Json

object KtorClient {

private val serializeJson = Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
}

val httpClient = HttpClient {
install(ContentNegotiation) {
json(json = serializeJson)
}

install(HttpRequestRetry) {
//function enables retrying a request if a 5xx response is received from a server and specifies the number of retries.
retryOnServerErrors(5)
//specifies an exponential delay between retries, which is calculated using the Exponential backoff algorithm.
exponentialDelay()
//If you want to add some additional params in header
modifyRequest { request ->
request.headers.append("x-retry-count", 2.toString())
}
}

//If you want to change user agent
install(UserAgent) {
agent = "Ktor"
}

install(DefaultRequest) {
headers.appendIfNameAbsent("X-custom-header", "Some Value")
contentType(ContentType.Application.Json)
}

ResponseObserver {
val timeDifference = it.responseTime.timestamp - it.requestTime.timestamp
val protocolVersion = it.version
}
}
}

If you want to get the response from the server in a well-formed model class instead of raw string then serializeJson becomes the most important aspect of httpClient as it enforces Kotlin type safety.
Here’s the list of all the available JSON configurations that you can use
https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/json.md#json-configuration

Now we are ready to get started, let’s do the GET request

A simple GET request

val response = KtorClient.httpClient.get("https://api.publicapis.org/entries").body<Sample>()

Well, in case you want the response in simple raw string then change body<Sample>() to body<String>() and that will do the work.
If you want to add some key-value pair in the headers or set cookies then it's very convenient to pass them along with the GET request.

val response = KtorClient.httpClient.get("https://api.publicapis.org/entries"){
//Set headers
headers {
append(HttpHeaders.Accept, "text/html")
append(HttpHeaders.Authorization, "abc123")
append(HttpHeaders.UserAgent, "ktor client")
}

//Set Cookies
cookie(name = "user_name", value = "jetbrains", expires = GMTDate(
seconds = 0,
minutes = 0,
hours = 10,
dayOfMonth = 1,
month = Month.DECEMBER,
year = 2023
)
)
}.body<Sample>()

A simple POST request
Just like the way we did the GET request making a POST request is also very straightforward.

val response = KtorClient.httpClient.post("https://api.publicapis.org/records") {
setBody(Customer("James", "12"))
contentType(ContentType.Application.Json)
}

As you delve deeper into Ktor Client, you’ll discover its rich set of features and customization options, allowing you to tailor it to your specific needs.

--

--

Pankaj Rai 🇮🇳

Software Engineer | GDE Android & Firebase | YouTuber — All Techies