はじめに
こんにちは!Android開発でHTTP通信といえば、長らくRetrofit一択でしたよね。私もずっとRetrofitを使ってきましたが、最近Ktorという選択肢に出会って、「これはすごい!」と感動しています😊
Ktorは、JetBrainsが開発したKotlinベースのHTTPクライアント・サーバーフレームワークです。最初は「また新しいライブラリか...」と思っていましたが、特にKotlin Multiplatformプロジェクトでは本当にその真価を発揮するんです!
「Retrofitじゃだめなの?」「学習コストどうなの?」そんな疑問を持ってる方も多いと思います。この記事では、私が実際に使ってみた経験をもとに、2025年最新の情報に基づいて、Android開発でのKtor HTTPクライアントの使い方を詳しく解説していきます。
Ktorの魅力的な特徴
⚡ 非同期処理への完全対応
Coroutineベースで設計されており、Retrofitよりも自然な非同期処理が書けます。
🎯 軽量設計
必要な機能だけを選択して追加できるプラグイン方式。「重たいライブラリは嫌だ」という方にピッタリ!
🌐 マルチプラットフォーム対応
Kotlin Multiplatformプロジェクトで同じHTTP通信コードを共有できるんです!JVM、Android、JavaScript、Nativeで動作。
🔧 Kotlinファースト
Kotlinの機能をフル活用して設計されているので、Kotlinらしいコードが書けます。
🔄 柔軟性
クライアント・サーバー両方で使用可能。
🆚 Retrofitとの比較
項目 | Ktor | Retrofit |
---|---|---|
言語 | Kotlin | Java (Kotlin対応) |
学習コスト | やや高い | 低い |
設定の自由度 | 高い | 中程度 |
アノテーション | 不要 | 必要 |
Kotlin Multiplatform対応 | ✅ | ❌ |
新規プロジェクトなら迷わずKtor、既存プロジェクトなら慎重に検討が良いかと思います。
基本的なHttpClientの作成
まずは一番シンプルな形から:
val client = HttpClient(Android)
「え?これだけ?」と最初は拍子抜けしました😅
シンプルなクライアントも良いですが、実際のアプリでは色々な設定が必要ですよね。Ktorの真価はここから発揮されます!
HttpClientの設定
class NetworkClient {
private val client = HttpClient(Android) {
install(ContentNegotiation) { // JSON シリアライゼーション
json(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
encodeDefaults = false
})
}
install(HttpTimeout) { // タイムアウト設定
requestTimeoutMillis = 15000
connectTimeoutMillis = 15000
socketTimeoutMillis = 15000
}
install(Logging) { // ログ出力
level = LogLevel.INFO
}
install(DefaultRequest) { // デフォルトリクエスト設定
header(HttpHeaders.ContentType, ContentType.Application.Json)
}
}
}
実装のポイント:
-
ignoreUnknownKeys = true
でAPIの変更で不要な設定値は無視できるので、今後バージョンアップで不要になった設定もエラーにならない - プラグイン方式なので必要な機能だけを追加
- 設定が直感的で分かりやすい
エンジンとしてAndroid
を指定していますが、これはAndroid専用の最適化されたエンジンです。他にCIO
(Coroutine I/O)やOkHttp
なども選択可能で、プロジェクトの要件に応じて変更できます。
GETリクエスト
// 基本的なGETリクエスト
suspend fun fetchUserData(userId: String): String {
val response: HttpResponse = client.get("https://api.example.com/users/$userId")
return response.bodyAsText()
}
// クエリパラメータ付きGET
suspend fun searchUsers(query: String, page: Int): String {
val response = client.get("https://api.example.com/users") {
parameter("q", query)
parameter("page", page)
parameter("limit", 20)
}
return response.bodyAsText()
}
POSTリクエスト
-
setBody(request)
で自動的にJSON変換 - フォームデータもサポート済み
- 戻り値も自動的に型変換される
@Serializable
data class CreateUserRequest(
val name: String,
val email: String,
val age: Int
)
@Serializable
data class User(
val id: Long,
val name: String,
val email: String,
val age: Int
)
// JSONでPOST
suspend fun createUser(request: CreateUserRequest): User {
return client.post("https://api.example.com/users") {
contentType(ContentType.Application.Json)
setBody(request)
}.body()
}
// フォームデータでPOST
suspend fun submitForm(name: String, email: String): String {
val response = client.submitForm(
url = "https://api.example.com/contact",
formParameters = parameters {
append("name", name)
append("email", email)
}
)
return response.bodyAsText()
}
PUTリクエスト・DELETEリクエスト
PUT、DELETEも全く同じパターンで書けます。
@Serializable
data class UpdateUserRequest(
val name: String?,
val email: String?,
val age: Int?
)
suspend fun updateUser(userId: Long, request: UpdateUserRequest): User {
return client.put("https://api.example.com/users/$userId") {
contentType(ContentType.Application.Json)
setBody(request)
}.body()
}
// DELETEリクエスト
suspend fun deleteUser(userId: Long): Boolean {
val response = client.delete("https://api.example.com/users/$userId")
return response.status.isSuccess()
}
ヘッダーの設定
実際のアプリでは認証トークンやカスタムヘッダーが必要ですよね。Ktorならこれも簡単です!
- 型安全な
HttpHeaders
定数も利用可能 - 単一ヘッダーは
header()
- 複数ヘッダーは
headers {}
ブロック
// Bearerトークン
suspend fun fetchProtectedData(token: String): String {
val response = client.get("https://api.example.com/protected") {
header(HttpHeaders.Authorization, "Bearer $token")
}
return response.bodyAsText()
}
// API Key
suspend fun fetchDataWithApiKey(apiKey: String): String {
val response = client.get("https://api.example.com/data") {
header("X-API-Key", apiKey)
}
return response.bodyAsText()
}
// カスタムヘッダー
suspend fun fetchWithCustomHeaders(): String {
val response = client.get("https://api.example.com/data") {
headers {
append("X-Custom-Header", "custom-value")
append("User-Agent", "MyApp/1.0")
}
}
return response.bodyAsText()
}
Retrofitだとヘッダー用のインターセプターを書く必要がありましたが、Ktorなら直接書けて楽チン!🎉
ファイルアップロード
import io.ktor.client.request.forms.*
import io.ktor.http.*
import java.io.File
suspend fun uploadFile(file: File): String {
val response = client.submitFormWithBinaryData(
url = "https://api.example.com/upload",
formData = formData {
append("file", file.readBytes(), Headers.build {
append(HttpHeaders.ContentType, "image/jpeg")
append(HttpHeaders.ContentDisposition, "filename=\"${file.name}\"")
})
}
)
return response.bodyAsText()
}
// マルチパートフォーム
suspend fun uploadWithMetadata(file: File, description: String): String {
val response = client.submitFormWithBinaryData(
url = "https://api.example.com/upload",
formData = formData {
append("description", description)
append("file", file.readBytes(), Headers.build {
append(HttpHeaders.ContentType, "image/jpeg")
append(HttpHeaders.ContentDisposition, "filename=\"${file.name}\"")
})
}
)
return response.bodyAsText()
}
例では、ファイルはfile.readBytes()
でByteArrayとして読み込んでいますが、大容量ファイルの場合はメモリ効率を考慮してストリーミングアップロードが推奨になります。
エラーハンドリング
基本的なエラーハンドリング
HTTPクライアントで一番重要なのがエラーハンドリング。Ktorは例外ベースで分かりやすいです。
suspend fun fetchDataWithErrorHandling(userId: String): Result<User> {
return try {
val user: User = client.get("https://api.example.com/users/$userId").body()
Result.success(user)
} catch (e: ClientRequestException) {
// 4xx エラー
when (e.response.status) {
HttpStatusCode.NotFound -> Result.failure(Exception("ユーザーが見つかりません"))
HttpStatusCode.Unauthorized -> Result.failure(Exception("認証が必要です"))
else -> Result.failure(Exception("クライアントエラー: ${e.response.status}"))
}
} catch (e: ServerResponseException) {
// 5xx エラー
Result.failure(Exception("サーバーエラー: ${e.response.status}"))
} catch (e: Exception) {
Result.failure(e)
}
}
Ktorの例外の種類:
-
ClientRequestException
: 4xxエラー(クライアント側の問題) -
ServerResponseException
: 5xxエラー(サーバー側の問題) -
RedirectResponseException
: 3xxリダイレクト -
ResponseException
: すべてのHTTPエラーの基底クラス
Retrofitと違って、HTTPステータスコードによって例外の種類が分かれているのが便利!4xx、5xxで処理を分けやすいです🎯
また、ネットワーク接続エラーやタイムアウトなどの通信エラーは通常のException
としてキャッチされます。これにより、HTTPレスポンスエラーとネットワークレベルのエラーを明確に区別できます。
まとめ
2025年の現在、KtorはバージョンV3.2.3となり、安定性とパフォーマンスが大幅に向上しています。Kotlin Multiplatformの普及に伴い、今後さらに重要になるライブラリの一つです。
まずは小さなプロジェクトから始めて、徐々にKtorの強力な機能を活用していきましょう!