Making API Calls in Android with Ktor

Mukesh RajputMukesh Rajput
3 min read

When developing Android applications, handling network operations is a common task. Ktor, a Kotlin-based HTTP client library, provides a robust and flexible way to make API calls. In this guide, we’ll walk through how to use Ktor to fetch data from an API in your Android app.

What is Ktor?

Ktor is a framework built by JetBrains for creating asynchronous applications, including HTTP clients and servers. It’s lightweight, highly customizable, and works seamlessly with Kotlin, making it an excellent choice for Android development.

Setting Up Ktor in Your Android Project

1. Add Dependencies

First, you need to add Ktor dependencies to your project. Open your build.gradle (or build.gradle.kts) file and include the necessary Ktor libraries:

// For Gradle Kotlin DSL (build.gradle.kts)
dependencies {
    implementation("io.ktor:ktor-client-core:2.4.0") // Check for the latest version
    implementation("io.ktor:ktor-client-android:2.4.0")
    implementation("io.ktor:ktor-client-serialization:2.4.0") // For JSON serialization
}

2. Configure the Ktor Client

Create a singleton object to configure and initialize the Ktor client. This client will be used throughout your app to make network requests.

import io.ktor.client.*
import io.ktor.client.engine.android.*
import io.ktor.client.features.json.*
import io.ktor.client.features.json.serializer.*

object NetworkClient {
    val client = HttpClient(Android) {
        install(JsonFeature) {
            serializer = KotlinxSerializer()
        }
    }
}

3. Create Data Class

Create data classes that represent the JSON structure of your API responses. For example, if you’re fetching user data, you might have:

import kotlinx.serialization.Serializable

@Serializable
data class User(
    val id: Int,
    val name: String,
    val email: String
)

4. Create a Repository for Network Operations

Create a repository class that will handle the network operations. This class will use the Ktor client to make API requests and process the responses.

import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*

class UserRepository {
    private val client = NetworkClient.client

    suspend fun getUser(userId: Int): User {
        return client.get("https://api.example.com/users/$userId")
    }

    suspend fun getAllUsers(): List<User> {
        return client.get("https://api.example.com/users")
    }
}

5. Fetch Data from the API in a ViewModel

In your ViewModel, use the repository to fetch data and expose it to the UI.

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class UserViewModel : ViewModel() {
    private val userRepository = UserRepository()

    val userData = MutableLiveData<User>()
    val allUsersData = MutableLiveData<List<User>>()

    fun fetchUser(userId: Int) {
        viewModelScope.launch {
            try {
                val user = userRepository.getUser(userId)
                userData.postValue(user)
            } catch (e: Exception) {
                // Handle error
            }
        }
    }

    fun fetchAllUsers() {
        viewModelScope.launch {
            try {
                val users = userRepository.getAllUsers()
                allUsersData.postValue(users)
            } catch (e: Exception) {
                // Handle error
            }
        }
    }
}

6. Display Data in Your Composable Function

Finally, in your Jetpack Compose UI, observe the data from the ViewModel and display it.

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun UserScreen(userViewModel: UserViewModel = viewModel()) {
    val userData by userViewModel.userData.observeAsState()
    val allUsersData by userViewModel.allUsersData.observeAsState()

    Column(modifier = Modifier.padding(16.dp)) {
        Button(onClick = { userViewModel.fetchAllUsers() }) {
            Text("Fetch All Users")
        }

        allUsersData?.let { users ->
            LazyColumn {
                items(users) { user ->
                    Text("Name: ${user.name}, Email: ${user.email}")
                }
            }
        }

        Button(onClick = { userViewModel.fetchUser(1) }) {
            Text("Fetch User with ID 1")
        }

        userData?.let { user ->
            Text("User: ${user.name}, Email: ${user.email}")
        }
    }
}

Conclusion

Using Ktor for API calls in an Android app allows you to leverage Kotlin’s modern features for networking tasks. By setting up a Ktor client, defining data models, creating a repository, and integrating with your ViewModel and Compose UI, you can build a robust and efficient network layer in your Android application.

Feel free to adjust the examples to fit your specific use case, and happy coding!

0
Subscribe to my newsletter

Read articles from Mukesh Rajput directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mukesh Rajput
Mukesh Rajput

Specializing in creating scalable and maintainable applications using MVVM and Clean Architecture principles. With expertise in Ktor, Retrofit, RxJava, View Binding, Data Binding, Hilt, Koin, Coroutines, Room, Realm, and Firebase, I am committed to delivering high-quality mobile solutions that provide seamless user experiences. I thrive on new challenges and am constantly seeking opportunities to innovate in the Android development space.