🌐

Kotlin OKHttp

2024/09/07に公開

👤対象者

  • androidで、HTTP通信をやってみたい人
  • OKHttpなるものを試したい人

Android開発でAPI通信するライブラリといえば、Retrofitが有名なパッケージですがOKHttpなるものあるようです。
気になるので試したみた👩‍⚕️

(offical)[https://square.github.io/okhttp/]

HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP efficiently makes your stuff load faster and saves bandwidth.

OkHttp is an HTTP client that’s efficient by default:

HTTP/2 support allows all requests to the same host to share a socket.
Connection pooling reduces request latency (if HTTP/2 isn’t available).
Transparent GZIP shrinks download sizes.
Response caching avoids the network completely for repeat requests.
OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service has multiple IP addresses, OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 and services hosted in redundant data centers. OkHttp supports modern TLS features (TLS 1.3, ALPN, certificate pinning). It can be configured to fall back for broad connectivity.

Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous blocking calls and async calls with callbacks.


HTTPは、現代のアプリケーションのネットワーク方法だ。データやメディアを交換する方法です。HTTPを効率的に使用することで、読み込みが速くなり、帯域幅を節約できます。

OkHttpはデフォルトで効率的なHTTPクライアントです:

HTTP/2のサポートにより、同じホストへのすべてのリクエストでソケットを共有できます。
コネクションプーリングはリクエストの待ち時間を短縮します(HTTP/2が利用できない場合)。
トランスペアレントGZIPはダウンロードサイズを縮小します。
レスポンスキャッシュにより、リクエストの繰り返しによるネットワークへの負荷を完全に回避します。
OkHttp はネットワークが面倒なときでも耐え忍びます: 一般的な接続の問題から静かに回復します。サービスに複数の IP アドレスがある場合、最初の接続に失敗すると、OkHttp は代替アドレスを試行します。これは IPv4+IPv6 や、冗長化されたデータセンターでホストされているサービスには必要です。OkHttp は最新の TLS 機能 (TLS 1.3、ALPN、証明書のピン留め) をサポートしています。広範な接続性のためにフォールバックするように設定できます。

OkHttpの使い方は簡単です。そのリクエスト/レスポンスAPIは、流暢なビルダーと不変性で設計されています。同期ブロック呼び出しとコールバックによる非同期呼び出しの両方をサポートしています。

早速使ってみる

ログを表示しるだけやってみようと思ったら、JSONが表示された笑
まあ、インターネットからデータを取得するのが目的なのでいいかなと。

build.gradle.ktsにパッケージを追加する
offcial

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
}

android {
    namespace = "com.junichi.httphilt"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.junichi.httphilt"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.1"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {

    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.lifecycle.runtime.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.ui)
    implementation(libs.androidx.ui.graphics)
    implementation(libs.androidx.ui.tooling.preview)
    implementation(libs.androidx.material3)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
    androidTestImplementation(platform(libs.androidx.compose.bom))
    androidTestImplementation(libs.androidx.ui.test.junit4)
    debugImplementation(libs.androidx.ui.tooling)
    debugImplementation(libs.androidx.ui.test.manifest)
    // add OkHttp dependencies
    // define a BOM and its version
    implementation(platform("com.squareup.okhttp3:okhttp-bom:4.12.0"))

    // define any required OkHttp artifacts without version
    implementation("com.squareup.okhttp3:okhttp")
    implementation("com.squareup.okhttp3:logging-interceptor")
}

AndroidManifest.xmlにパーミッションの許可を設定して、外部と通信できるようにする。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" >
    <!-- インターネットへのアクセス許可-->
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.HttpHilt"
        tools:targetApi="31" >
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.HttpHilt" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

MainActivity.ktを以下のように修正

package com.junichi.httphilt

import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.junichi.httphilt.ui.theme.HttpHiltTheme
import okhttp3.*
import java.io.IOException

class MainActivity : ComponentActivity() {
    private val client = OkHttpClient()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            HttpHiltTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    var responseText by remember { mutableStateOf("Fetching data...") }

                    LaunchedEffect(Unit) {
                        fetchData { result ->
                            responseText = result
                        }
                    }

                    Text(
                        text = responseText,
                        modifier = Modifier.padding(16.dp)
                    )
                }
            }
        }
    }

    private fun fetchData(callback: (String) -> Unit) {
        val request = Request.Builder()
            .url("https://jsonplaceholder.typicode.com/posts")
            .build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.e("MainActivity", "Request failed: ${e.message}")
                callback("Failed to fetch data")
            }

            override fun onResponse(call: Call, response: Response) {
                response.use {
                    if (!response.isSuccessful) {
                        Log.e("MainActivity", "Unexpected response code: ${response.code}")
                        callback("Unexpected response: ${response.code}")
                    } else {
                        val responseBody = response.body?.string()
                        Log.d("MainActivity", "Response: $responseBody")
                        callback("Response: $responseBody")
                    }
                }
            }
        })
    }
}

感想

チュートリアルを見るといきなりMVVMだったりするので、こんな感じの機能を試すだけのサンプル欲しかったので作りました。
このパッケージは主流なのかな?

Discussion