Retrofit vs. Ktor Client

Gokcensolmaz
6 min readFeb 5, 2024

Navigating the Old Guard and the Newcomer in Android HTTP Requests?

When developing an Android application, we use HTTP requests to retrieve data by communicating with the backend. The data we retrieve from the backend via HTTP requests is stored and processed within the application to increase its functionality. To create HTTP requests, Android developers can use frameworks or libraries that implement all the necessary implementations.

The leading library when it comes to handling HTTP requests is Retrofit. The existence of other frameworks and libraries that can be used besides Retrofit guides us to choose the right tool for our needs. In this article, I will make a comparison between Retrofit, the most used library, and KTor, a Kotlin-focused framework.

Retrofit VS Ktor Client: For Android HTTP Requests
Retrofit VS Ktor-Client: For Android HTTP Requests

Retrofit, developed by Square, proved itself due to simplicity and effectiveness in consuming RESTful APIs. Retrofit ensures type safety in the context of HTTP requests and responses. This approach ensures that the compiler can catch type mismatches and inconsistencies at compile time. Retrofit using various serialization libraries such as Gson and Moshi to simplify the process of parsing JSON responses into objects. Its concise syntax and focus on HTTP Client operations makes it more popular.

Retrofit also supports asynchronous programming. When dealing with network requests, it’s important that these operations are performed asynchronously to prevent blocking the main thread, ensuring that the application remains responsive to user interactions. Retrofit’s approach to asynchronous programming relied on callbacks. When you make a network request, Retrofit allows you to handle the response or error within callbacks (onResponse and onFailure), this way the main thread remains unblocked and the application remains responsive. Over time, Retrofit has evolved to support modern asynchronous programming paradigms, through integration with Kotlin Coroutines. This integration enables developers to make network requests in an easy way with sequential manner and improving code readablitiy and maintainability with avoiding callback complexity.

Ktor is an open-source, asynchronous, lightweight and flexible framework for building web applications in Kotlin. Positioned as a modern web framework, Ktor enables developers to create strong server-side applications and microservices. Developers can handle both network requests and backend technologies with Ktor. This includes defining routes, adding middleware, and managing complex server logic in a clear way. Ktor comes with built-in support for serialization through kotlinx.serialization, which is developed by JetBrains specifically for Kotlin. So, while Retrofit relies on external libraries for serialization, Ktor leverages its own feature for this purpose. It offers a comprehensive set of tools for building robust server-side components, making it perfect for developers who need a unified platform for both client-side and server-side tasks.

Additionally, Ktor uses Kotlin’s coroutines for asynchronous programming, making IO operations fast and efficient. Ktor Client supports making requests using suspending functions, which are a fundamental part of Kotlin’s coroutines. This approach allows for more straightforward asynchronous code that is easy to read and write, without the need for additional libraries or adapters to handle asynchronous operations. Ktor’s design approach embraces Kotlin’s coroutine system, making asynchronous programming a core part of its functionality. This native integration with coroutines means that asynchronous code in Ktor is more suitable to Kotlin, providing seamless support for managing concurrent operations and composing asynchronous logic.

Although Ktor is often known for its server capabilities, it also includes a powerful client library known as Ktor Client. The Ktor Client makes it easy for developers to send HTTP requests, work with web services, and communicate with APIs, through Kotlin’s expressive language features.

Since Ktor is a multi-platform framework, the Ktor Client uses an engine to handle the requests. To create a Ktor Client on Android, you instantiate it with the “Android” Engine. Different engines support different platforms.

But Ktor is relatively new and doesn’t have as much strong community as Retrofit, so using Ktor in difficult and complex use cases needs courage as if you are stuck in an unexpected problem, there isn’t much documentation. In other hand, Retrofit has a strong community, loads and loads of documentations and support. However, it’s worth noting that the open-source nature of both tools means that community support can rapidly evolve. JetBrains’ commitment to Ktor does suggest that its documentation and ecosystem will continue to expand, potentially narrowing this gap over time.

To better illustrate the differences between the two tools in terms of syntax, I will provide code examples that show how to send a GET Request. For this purpose, I’ve selected the API provided by rickandmortyapi.com . I chose this API because I plan to continue using it in my upcoming article, where I’ll discuss libraries for fetching images using the same API.

Client Initialization

Ktor: In the heart of utilizing Ktor Client in Android development is initializing the client with the appropriate configurations. The following snippet demonstrates how to instantiate the HttpClient using the Androidengine, which is tailored for Android applications. Additionally, it showcases the installation of the ContentNegotiation plugin with JSON serialization. This setup is crucial for enabling the client to automatically handle JSON data formats, streamlining the process of communicating with web services.

private val client = HttpClient(Android) {
install(ContentNegotiation) {
json()
}
}

Retrofit: The initialization of the Retrofit client marks the first step in setting up a robust communication with web services in Android development. The code snippet below demonstrates how to create a Retrofit instance. This instance is configured with the base URL of the ‘Rick and Morty API’ and utilizes the Gson converter factory for JSON serialization. This configuration is essential for ensuring seamless conversion of JSON data into Kotlin objects, thereby simplifying the task of data handling in the application.

private val retrofit = Retrofit.Builder()
.baseUrl("https://rickandmortyapi.com/api/")
.addConverterFactory(GsonConverterFactory.create())
.build()

Making an HTTP Request

Ktor: This function illustrates how to perform a GET request using Ktor Client to fetch a character’s details from the ‘Rick and Morty API’. The function getCharacter takes a character ID as a parameter and returns the character details. This example highlights the simplicity and power of Ktor Client in executing HTTP requests and processing the responses.

private suspend fun getCharacter(characterId: String): Character? {
val character = client
.get("https://rickandmortyapi.com/api/character/$characterId")
return character.body()
}

Retrofit: Defining the interface for HTTP requests is a cornerstone of using Retrofit. The following snippet illustrates how to declare an interface using Retrofit annotations, which describe the HTTP operations. Here, a GET request is defined to fetch a character’s details from the ‘Rick and Morty API’ by specifying the character ID as a dynamic URL path. This approach encapsulates the API endpoints as interface methods, making the code clean, concise, and easy to maintain.

interface RickAndMortyApi {
@GET("character/{id}")
fun getCharacter(@Path("id") characterId: String): Call<Character>
}

...

class MainActivity : ComponentActivity(){
...
private val api = retrofit.create(RickAndMortyApi::class.java)
private fun getCharacter(characterId: String) {
api.getCharacter(characterId).enqueue(object : Callback<Character> {
override fun onResponse(call: Call<Character>, response: Response<Character>) {
if (response.isSuccessful) {
characterInfoLiveData.postValue(response.body())
}
}

override fun onFailure(call: Call<Character>, t: Throwable) {
TODO("Not yet implemented")
}
})
}
...
}

Calling the HTTP Request Function

Ktor: The following snippet demonstrates how to invoke the getCharacter function when a user clicks a button. This operation is performed within a coroutine launched by lifecycleScope, which is part of the Android Jetpack's Lifecycle library. This approach ensures that the network request is made in a non-blocking way, adhering to best practices for asynchronous programming in Android. The runOnUiThread method is then used to update the UI with the fetched data, showcasing a practical application of Ktor Client in a real-world scenario.

Button(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
onClick = {
lifecycleScope.launch {
val character = getCharacter(characterIdInput)
runOnUiThread {
characterInfoLiveData.value = character
}
}
}
) {
Text(text = "Get Character")
}

Retrofit:

Button(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
onClick = {
if (characterIdInput.isNotEmpty()) {
getCharacter(characterIdInput)
}
}
) {
Text(text = "Get Character")
}
Application UI Transformation: Before and After Executing a Ktor GET Request to Fetch ‘Rick and Morty’ Character Details.

In conclusion, Retrofit and Ktor Client offer different advantages for handling HTTP requests in Android applications. The Retrofit stands out for its simplicity, type safety, and strong community support. Although Ktor Client is newer, it has flexibility, Kotlin coroutine-based asynchronous programming, and built-in serialization support. While Retrofit currently has a larger community, Ktor Client’s multiplatform capabilities point to its potential growth. The choice depends on project needs and familiarity with the tools, but both offer solid solutions for efficient networking in Kotlin-based Android development.

--

--