Exploring Ktor: A Modern Networking Framework for Kotlin

Ktor is an asynchronous networking framework developed by JetBrains, designed for building both server and client applications in Kotlin. While Retrofit has been the go-to standard for Android networking for many developers, Ktor is predicted to be the future of networking in Kotlin.

The traditional reliance on Retrofit has placed many Android developers in a comfort zone. Although Retrofit offers numerous benefits, this article will not delve into those advantages or compare Ktor and Retrofit directly. Instead, we will focus on how to effectively use Ktor in your application.

Ultimately, the choice between Ktor and Retrofit depends on project requirements, scope, and the developer’s preferences. Each framework has its own pros and cons, so let’s dive into how to set up Ktor in your project.

Project Setup and Dependencies

  1. Add Ktor and Serialization Plugins
    To get started, you need to add the Kotlin Serialization plugin in your project’s build.gradle.kts file (root):
plugins {
    id("org.jetbrains.kotlin.plugin.serialization") version "2.1.20"
}
  1. Add Ktor Dependencies

Next, in your app-level build.gradle.kts, include the necessary Ktor dependencies:kotli

plugins {
    id("kotlinx-serialization")
}

dependencies {
    implementation(platform("io.ktor:ktor-bom:3.1.2"))
    implementation("io.ktor:ktor-client-android")
    implementation("io.ktor:ktor-client-serialization")
    implementation("io.ktor:ktor-client-logging")
    implementation("io.ktor:ktor-client-content-negotiation")
    implementation("io.ktor:ktor-serialization-kotlinx-json")
}

Creating a Reusable HttpClient
Before making requests, it’s essential to create a reusable HttpClient. This can be done using a singleton pattern or dependency injection (DI) frameworks like Koin or Hilt.

Benefits of a Reusable Client
Persistent Connections: A reusable client maintains persistent connections (keep-alive), which reduces latency for subsequent requests by avoiding the overhead of establishing new connections.
Kotlin Multiplatform Compatibility: If you are using Kotlin Multiplatform, reusing a Ktor client allows you to share networking logic across Android, iOS, and other platforms, minimizing code duplication and ensuring consistent behavior.
Resource Management: Managing a single client simplifies cleanup (e.g., calling client.close()) when your app or module is shutting down, preventing resource leaks.
Example of Creating a Client Instance
Here’s how you can create a client instance:

private const val NETWORK_TIME_OUT = 15000

val httpClient = HttpClient(Android) {
    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            isLenient = true
            ignoreUnknownKeys = true
        })
    }
    install(HttpTimeout) {
        requestTimeoutMillis = NETWORK_TIME_OUT
        connectTimeoutMillis = NETWORK_TIME_OUT
        socketTimeoutMillis = NETWORK_TIME_OUT
    }
    install(Logging) {
        logger = object : Logger {
            override fun log(message: String) {
                Log.v("KtorLogger", message)
            }
        }
        level = LogLevel.ALL
    }
    install(DefaultRequest) {
        header(HttpHeaders.ContentType, ContentType.Application.Json)
    }
    defaultRequest {
        contentType(ContentType.Application.Json)
        accept(ContentType.Application.Json)
    }
}

Making Requests with Ktor
Example: GET Request
To fetch a list of locations, you can use the following function:

suspend fun fetchLocationsList(): User {
    return httpClient.get("https://api.example.com/locations/123").body()
}

To create a new location, you can use this function:

suspend fun createLocationList(location: Location): HttpResponse {
    return httpClient.post("https://api.example.com/locations") {
        contentType(ContentType.Application.Json)
        setBody(location)
    }
}

Creating Data Models
When creating data models, you need to use the @Serializable annotation from Kotlinx. Here’s an example:

@Serializable
data class Location(
    val locationId: Int,
    val locationName: String,
    val locationLatLong: String
)

Note
The serialization plugin has already been added in the app-level build.gradle.kts.

Logging and Debugging
Ktor provides a logging feature that allows you to monitor requests and responses in Logcat. It is also highly compatible with DI frameworks like Koin and Hilt. For logging purposes, you can integrate libraries such as Timber or Klogging.

Final Thoughts
Choosing between Retrofit and Ktor depends on your project’s requirements, scope, and your team’s familiarity with the tools. Retrofit remains a solid, reliable choice for traditional Android apps, while Ktor excels in modern, Kotlin-first, and multiplatform environments. If you’re ready to explore Ktor, the steps above will help you get started quickly and efficiently.

Stay tuned for future articles where we’ll dive deeper into integrating Ktor with dependency injection (Koin) and advanced logging using Timber.

Ready to modernize your Android networking stack? Give Ktor a try and experience the flexibility of Kotlin-first development.

Leave a Reply