avatarYanneck Reiß

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

5132

Abstract

respective engine.</p><p id="8d8a">Because we are in an Android project, of course, we need to configure the <code>Android</code> engine.</p><div id="43f8"><pre><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createKtorHttpClient</span><span class="hljs-params">()</span></span>: HttpClient = HttpClient(Android) { .. <span class="hljs-comment">// Our installations</span> }</pre></div><p id="b41d">Now inside the <code>HttpClientConfig</code> block, we can add (<i>install</i>) the plugins we want to configure for our <code>HttpClient</code>.</p><h2 id="8d02">3.1 Timeout</h2><p id="6051">The first thing we want to configure is the connection time-out. That is the maximum time a connection with a server stays active. The configuration time gets served in milliseconds.</p><p id="edae">In the example below, we set both, <code>connectTimeout</code> and <code>socketTimeout</code> to 30 milliseconds.</p><div id="0b93"><pre><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">provideKtorHttpClient</span><span class="hljs-params">()</span></span>: HttpClient = HttpClient(Android) { engine { connectTimeout = <span class="hljs-number">30_000</span> socketTimeout = <span class="hljs-number">30_000</span> } .. <span class="hljs-comment">// Other configurations</span> }</pre></div><h2 id="f539">3.2 JSON Serialization/Deserialization</h2><p id="aa22">Next, we add the <code>ContentNegotiation</code>. Inside the configuration block, we enable the <code>json</code> configuration. This way we can automatically serialize and deserialize requests and responses respectively.</p><div id="ac28"><pre><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">provideKtorHttpClient</span><span class="hljs-params">()</span></span>: HttpClient = HttpClient(Android) { .. <span class="hljs-comment">// Other configurations</span>

install(ContentNegotiation) {
    json(Json {
        prettyPrint = <span class="hljs-literal">true</span>
        isLenient = <span class="hljs-literal">true</span>
        ignoreUnknownKeys = <span class="hljs-literal">true</span>
    })
}

}</pre></div><h2 id="b1b1">3.3 Logging</h2><p id="813b">We want to be able to log our HTTP calls. Therefore we add the Logging plugin to the configuration</p><div id="dfb1"><pre><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">provideKtorHttpClient</span><span class="hljs-params">()</span></span>: HttpClient = HttpClient(Android) { .. <span class="hljs-comment">// Other configurations</span> install(Logging) { logger = <span class="hljs-keyword">object</span> : Logger { <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">log</span><span class="hljs-params">(message: <span class="hljs-type">String</span>)</span></span> { <span class="hljs-comment">// Log the message with the framework of your choice</span> }

    }
    level = LogLevel.ALL
}

}</pre></div><p id="6f75">To also be able to log the responses we can add the following:</p><div id="f07c"><pre><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">provideKtorHttpClient</span><span class="hljs-params">()</span></span>: HttpClient = HttpClient(Android) { .. <span class="hljs-comment">// Other configurations</span> install(ResponseObserver) { onResponse { response: HttpResponse -> <span class="hljs-comment">// Log the response with the logging framework of your choice</span> } } }</pre></div><h2 id="ddcf">3.4 Default Request Configuration</h2><p id="8207">We can also configure default values for all requests we execute with our <code>HttpClient</code>. For example, we can provide some default headers that should be present in all requests.</p><p id="3444">But that’s not all, here is also the place where we can finally set up our base URL that we previously configured in section 1.3 of this article.</p><div id="4f7a"><pre>fun <span class="hljs-built_in">provideKtorHttpClient</span>(): HttpClient = <span class="hljs-built_in">HttpClient</span>(Android) { .. <span class="hljs-comment">// Other configurations</span> <span class="hljs-built_in">install</span>(DefaultRequest) { <span class="hljs-built_in">url</span>(BuildConfig.BASE_URL) <span class="hljs-selector-tag">header</span>(HttpHeaders.ContentType, ContentType.Application.Json) } }</pre></div><h1 id="e58e">4 Creating Our First Request</h1><p id="ad54">Now that we have our configuration in place, let’s take a look at an example of how we can communicate with a sample API.</p><p id="07cf">Making a request is quite simple. Let’s take an ordinary <code>ViewModel</code> as an example, where we want to execute our request.</p><p id="77a2">As an example, we use open accessible <a href="https://v2.jokeapi.dev/joke/Any?safe-mo

Options

de">joke API</a>. This API randomly generates a joke and requires no authentication.</p><p id="eb4c">We define a DTO for the response like the following <code>data class</code> shows:</p><div id="1c96"><pre><span class="hljs-keyword">import</span> kotlinx.serialization.Serializable

<span class="hljs-meta">@Serializable</span> <span class="hljs-keyword">data</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JokeDTO</span>( <span class="hljs-keyword">val</span> joke: String? = <span class="hljs-literal">null</span>, <span class="hljs-keyword">val</span> answer: String? = <span class="hljs-literal">null</span>, <span class="hljs-keyword">val</span> setup: String? = <span class="hljs-literal">null</span>, <span class="hljs-keyword">val</span> delivery: String? = <span class="hljs-literal">null</span> )</pre></div><p id="c78f">Note that we need to add the <code>@Serialization</code> annotation from the <code>kotlinx.serialization</code> plugin.</p><p id="feb5">To actually execute the <code>GET</code> request to this API, we can call <code>get()</code> at an instance of our Ktor <code>HttpClient</code>.</p><div id="e37e"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">JokeViewModel</span>( <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> httpClient: HttpClient ) : ViewModel() { <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">loadJoke</span><span class="hljs-params">()</span></span> { viewModelScope.launch(Dispatchers.IO) { <span class="hljs-keyword">val</span> jokeDTO: JokeDTO = httpClient .<span class="hljs-keyword">get</span>() .body()

        .. <span class="hljs-comment">// further process the jokeDTO</span>
    }
}

}</pre></div><p id="141f">In the example above, we provide an instance of our previously configured <code>HttpClient</code> to the <code>JokeViewModel</code> via its constructor.</p><p id="9afd">Assuming that we provided the joke API URL as our base URL, after calling <code>get()</code> we now can directly convert the body of the <code>HttpResponse</code> to the respective <code>DTO</code> we expect from a request. In our case the <code>JokeDTO</code>.</p><p id="a753">Note that the call had to be executed in a coroutine scope.</p><h1 id="b275">Conclusion</h1><p id="23a4">If you’d ask me, “should I use Ktor over Retrofit”? I could only give you the unsatisfying answer: “it depends”.</p><p id="942e">Ktor comes with the benefit that it’s fully built on Kotlin, has built-in support for WebSockets, authorization can be greatly further customized with various components.</p><p id="5e48">But the major benefit is of course that Ktor, other than Retrofit, can be used in a Kotlin Multiplatform project as an HTTP Client. So if you plan someday to migrate your project to a Kotlin Multiplatform Mobile project, the migration process would be way easier.</p><p id="c2ea">On the other hand, while Retrofit is still the industry standard, Ktor also has no clear direct benefit if you just use it as a traditional HTTP Client for REST communication which is probably required in most projects.</p><p id="37bc">In the end, it comes down to your personal preference. If you’re starting a new Android project, consider giving Ktor a try. But if you have an older project, the effort required to migrate from one framework to another may not be worth it.</p><p id="a8d5"><b>I hope you had some takeaways, clap if you liked my article, make sure to subscribe to get notified via e-mail,</b> <b>and follow for more!</b></p><div id="2d17" class="link-block"> <a href="https://readmedium.com/speed-up-your-jetpack-compose-development-with-the-compose-helper-ide-plugin-b21f35a4df39"> <div> <div> <h2>Speed Up Your Jetpack Compose Development With the Compose Helper IDE plugin</h2> <div><h3>Solve Three Common Pain Points in Android Development with Jetpack Compose</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*_KzprmtsAiDyUpFFCjtewg.png)"></div> </div> </div> </a> </div><div id="50e6" class="link-block"> <a href="https://readmedium.com/mastering-execution-time-measurement-in-kotlin-optimize-your-applications-for-a-seamless-user-e83e6a97c98c"> <div> <div> <h2>Mastering Execution Time Measurement in Kotlin</h2> <div><h3>Learn to Measure and Improve the Performance of Your Functions for a Seamless User Experience</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*RphZHnL1SthZBE8N8MynDQ.png)"></div> </div> </div> </a> </div></article></body>

Exploring Ktor: An Alternative To Retrofit For HTTP Requests In Android

A Guide Through Integrating The Ktor Client Into Your Native Android Project

Although Ktor has already won great popularity among developers for Kotlin Multiplatform Projects, the leading framework used for processing HTTP requests is still Retrofit.

However, looking at alternative solutions to your current tech stack never hurts. So in this article, we will look at how to set up the Ktor client in your native Android project, handle HTTP requests and finally discuss whether you should use Ktor over Retrofit for your current native Android project.

1 Gradle Setup

To be able to properly configure and use our Ktor client, we first need to enrich our Gradle configuration with a few dependencies.

1.1 Client dependency

The first thing we need to do is to add the dependency for the Ktor Client Android engine.

dependencies {
    implementation "io.ktor:ktor-client-android:2.2.3"
}

1.2 Logging

The Logging plugin allows us to log our HTTP calls. To be later able to add it to our configuration, add the following to your project-level build.gradle file:

dependencies {
    implementation "io.ktor:ktor-client-logging:2.2.3"
}

1.2 JSON Serialization/Deserialization

For serialization, we need to add the Kotlin serialization plugin to our project. To do so, add the following to your project-level build.gradle file:

buildscript {
    ext.your_kotlin_version = '1.8.0'
    repositories { mavenCentral() }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-serialization:$your_kotlin_version"
    }
}

Next, we need to add the Ktor Content Negotiation to our app module build.gradle dependencies.

For serialization and deserialization of JSON data, we have the option to use GSON, Jackson, or kotlinx.serialization. In our case, we will use kotlinx.serialization.

We will therefore add the content negotiation and kotlinx.serialization dependency to the dependencies block as you can see in the following code snippet:

dependencies {
    implementation "io.ktor:ktor-client-content-negotiation:2.2.3"
    implementation "io.ktor:ktor-serialization-kotlinx-json:2.2.3"
}

1.3 Setup The Base URL

A common thing to do is to provide the base URL of our respective API to the build configuration, so it changes depending on our respective build flavors.

Let’s assume we have a dev and a prod flavor. One for development and the other for production.

To add a new field to the build configuration for each of those flavors, we can add a new build config field to our productFlavors section in the app module build.gradle file.

android {
    ..
    
    flavorDimensions("app")
        productFlavors {
            dev {
                dimension = "app"
                applicationId "dev.com.your.project"
                buildConfigField 'String', 'BASE_URL', '"https://dev.yourapi.com"'
            }
            prod {
                dimension = "app"
                applicationId "com.your.project"
                buildConfigField 'String', 'BASE_URL', '"https://yourapi.com"'
            }
        }
    }
}

3 Creating the Client Configuration

Now that we have our Gradle setup in place, let’s proceed with the actual setup of the HttpClient.

Just like we need to configure a OkHttpClient and create Retrofit instance with Retrofit, we need to configure a HttpClient for a respective engine.

Because we are in an Android project, of course, we need to configure the Android engine.

fun createKtorHttpClient(): HttpClient = HttpClient(Android) {
       .. // Our installations
}

Now inside the HttpClientConfig block, we can add (install) the plugins we want to configure for our HttpClient.

3.1 Timeout

The first thing we want to configure is the connection time-out. That is the maximum time a connection with a server stays active. The configuration time gets served in milliseconds.

In the example below, we set both, connectTimeout and socketTimeout to 30 milliseconds.

fun provideKtorHttpClient(): HttpClient = HttpClient(Android) {
    engine {
        connectTimeout = 30_000
        socketTimeout = 30_000
    }
    .. // Other configurations
}

3.2 JSON Serialization/Deserialization

Next, we add the ContentNegotiation. Inside the configuration block, we enable the json configuration. This way we can automatically serialize and deserialize requests and responses respectively.

fun provideKtorHttpClient(): HttpClient = HttpClient(Android) {
    .. // Other configurations

    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            isLenient = true
            ignoreUnknownKeys = true
        })
    }
}

3.3 Logging

We want to be able to log our HTTP calls. Therefore we add the Logging plugin to the configuration

fun provideKtorHttpClient(): HttpClient = HttpClient(Android) {
     .. // Other configurations
    install(Logging) {
        logger = object : Logger {
            override fun log(message: String) {
                // Log the message with the framework of your choice
            }
    
        }
        level = LogLevel.ALL
    }
}

To also be able to log the responses we can add the following:

fun provideKtorHttpClient(): HttpClient = HttpClient(Android) {
     .. // Other configurations
    install(ResponseObserver) {
        onResponse { response: HttpResponse ->
          // Log the response with the logging framework of your choice
        }
    }
}

3.4 Default Request Configuration

We can also configure default values for all requests we execute with our HttpClient. For example, we can provide some default headers that should be present in all requests.

But that’s not all, here is also the place where we can finally set up our base URL that we previously configured in section 1.3 of this article.

fun provideKtorHttpClient(): HttpClient = HttpClient(Android) {
     .. // Other configurations
     install(DefaultRequest) {
          url(BuildConfig.BASE_URL)
          header(HttpHeaders.ContentType, ContentType.Application.Json)
      }
}

4 Creating Our First Request

Now that we have our configuration in place, let’s take a look at an example of how we can communicate with a sample API.

Making a request is quite simple. Let’s take an ordinary ViewModel as an example, where we want to execute our request.

As an example, we use open accessible joke API. This API randomly generates a joke and requires no authentication.

We define a DTO for the response like the following data class shows:

import kotlinx.serialization.Serializable

@Serializable
data class JokeDTO(
    val joke: String? = null,
    val answer: String? = null,
    val setup: String? = null,
    val delivery: String? = null
)

Note that we need to add the @Serialization annotation from the kotlinx.serialization plugin.

To actually execute the GET request to this API, we can call get() at an instance of our Ktor HttpClient.

class JokeViewModel(
  private val httpClient: HttpClient
) : ViewModel() {
    fun loadJoke() {
        viewModelScope.launch(Dispatchers.IO) {
            val jokeDTO: JokeDTO = httpClient
                .get()
                .body()
            
            .. // further process the jokeDTO
        }
    }
}

In the example above, we provide an instance of our previously configured HttpClient to the JokeViewModel via its constructor.

Assuming that we provided the joke API URL as our base URL, after calling get() we now can directly convert the body of the HttpResponse to the respective DTO we expect from a request. In our case the JokeDTO.

Note that the call had to be executed in a coroutine scope.

Conclusion

If you’d ask me, “should I use Ktor over Retrofit”? I could only give you the unsatisfying answer: “it depends”.

Ktor comes with the benefit that it’s fully built on Kotlin, has built-in support for WebSockets, authorization can be greatly further customized with various components.

But the major benefit is of course that Ktor, other than Retrofit, can be used in a Kotlin Multiplatform project as an HTTP Client. So if you plan someday to migrate your project to a Kotlin Multiplatform Mobile project, the migration process would be way easier.

On the other hand, while Retrofit is still the industry standard, Ktor also has no clear direct benefit if you just use it as a traditional HTTP Client for REST communication which is probably required in most projects.

In the end, it comes down to your personal preference. If you’re starting a new Android project, consider giving Ktor a try. But if you have an older project, the effort required to migrate from one framework to another may not be worth it.

I hope you had some takeaways, clap if you liked my article, make sure to subscribe to get notified via e-mail, and follow for more!

Ktor
Android
Android App Development
Programming
Coding
Recommended from ReadMedium