OkHttp

Android開発者Square社製のJava/Kotlin向け高性能HTTPクライアント。HTTP/2サポート、接続プーリング、GZIP圧縮、レスポンスキャッシング、ネットワーク障害からの自動復旧機能を内蔵。Android及びJVMプラットフォームで最適化。

HTTPクライアントAndroidJavaKotlin高性能同期非同期

GitHub概要

square/okhttp

Square’s meticulous HTTP client for the JVM, Android, and GraalVM.

スター46,701
ウォッチ1,615
フォーク9,253
作成日:2012年7月23日
言語:Kotlin
ライセンス:Apache License 2.0

トピックス

androidgraalvmjavakotlin

スター履歴

square/okhttp Star History
データ取得日時: 2025/10/22 10:04

ライブラリ

OkHttp

概要

OkHttpは「Android・Java向けの高性能HTTPクライアント」として開発された、Square社製のオープンソースライブラリです。AndroidアプリケーションでのHTTP通信で事実上の業界標準として広く採用され、同期・非同期の両方のAPIをサポート。HTTP/2対応、自動的な接続プーリング、透明なGZIP圧縮、レスポンスキャッシングを内蔵し、効率的なネットワーク通信を実現。Retrofit、Picasso等の主要ライブラリの基盤技術として使用され、Androidエコシステムの中核コンポーネントとして確立されています。

詳細

OkHttp 2025年版はAndroid開発における定番HTTPクライアントとして進化を続け、最新のHTTP仕様とAndroidプラットフォームに最適化されています。HTTP/1.1とHTTP/2の完全サポート、SPDY対応、WebSocket機能、カスタマイズ可能なInterceptorシステムにより、企業レベルのモバイルアプリケーション開発を支援。ConnectionPool による効率的な接続管理、自動的なリトライ機能、詳細なログ機能により、安定性とパフォーマンスを両立。AndroidアプリのネットワークレイヤーにおいてSquareエコシステムとの統合による開発効率向上も実現しています。

主な特徴

  • Android最適化: Androidプラットフォームに特化した設計と最適化
  • HTTP/2完全対応: マルチプレクシングと効率的な接続利用
  • 接続プーリング: 自動的な接続再利用とパフォーマンス向上
  • Interceptorシステム: 柔軟なリクエスト・レスポンス処理カスタマイズ
  • 透明キャッシング: 自動的なレスポンスキャッシュとオフライン対応
  • WebSocket対応: リアルタイム通信機能の内蔵サポート

メリット・デメリット

メリット

  • Android開発における事実上の標準で豊富な実績と信頼性
  • HTTP/2対応による高速化とバッテリー消費最適化
  • 接続プーリングと自動リトライによる堅牢なネットワーク処理
  • Interceptorシステムによる柔軟なカスタマイズ性
  • Square製エコシステム(Retrofit、Moshi等)との優れた統合
  • 詳細なログ機能により効率的なデバッグが可能

デメリット

  • Android・Javaプラットフォーム専用でクロスプラットフォーム対応不可
  • 学習コストが高く単純なHTTPリクエストにはオーバーヘッド
  • APKサイズの増加によりアプリサイズが大きくなる
  • 設定項目が多く初期セットアップが複雑
  • Kotlinのコルーチンとの連携で追加の考慮事項が必要
  • Web環境(JavaScript)では利用不可

参考ページ

書き方の例

インストールと基本セットアップ

// build.gradle (Module: app)
dependencies {
    // OkHttpの最新バージョン
    implementation 'com.squareup.okhttp3:okhttp:4.12.0'
    
    // ログ機能付きバージョン(デバッグ時推奨)
    implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
    
    // WebSocket機能(必要に応じて)
    implementation 'com.squareup.okhttp3:okhttp-ws:4.12.0'
}
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

基本的なリクエスト(GET/POST/PUT/DELETE)

import okhttp3.*;
import java.io.IOException;

public class OkHttpExample {
    private final OkHttpClient client = new OkHttpClient();

    // 基本的なGETリクエスト(同期)
    public void basicGetRequest() throws IOException {
        Request request = new Request.Builder()
                .url("https://api.example.com/users")
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                String responseBody = response.body().string();
                System.out.println("レスポンス: " + responseBody);
                
                // ヘッダー情報の取得
                System.out.println("Content-Type: " + response.header("Content-Type"));
                System.out.println("ステータスコード: " + response.code());
            } else {
                System.out.println("リクエスト失敗: " + response.code());
            }
        }
    }

    // 非同期GETリクエスト
    public void asyncGetRequest() {
        Request request = new Request.Builder()
                .url("https://api.example.com/users")
                .build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                System.out.println("リクエスト失敗: " + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String responseBody = response.body().string();
                    System.out.println("非同期レスポンス: " + responseBody);
                } else {
                    System.out.println("非同期リクエスト失敗: " + response.code());
                }
                response.close();
            }
        });
    }

    // POSTリクエスト(JSON送信)
    public void postJsonRequest() throws IOException {
        MediaType JSON = MediaType.get("application/json; charset=utf-8");
        String json = "{"
                + "\"name\": \"田中太郎\","
                + "\"email\": \"[email protected]\","
                + "\"age\": 30"
                + "}";

        RequestBody body = RequestBody.create(json, JSON);
        Request request = new Request.Builder()
                .url("https://api.example.com/users")
                .post(body)
                .addHeader("Authorization", "Bearer your-jwt-token")
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                String responseBody = response.body().string();
                System.out.println("作成されたユーザー: " + responseBody);
            } else {
                System.out.println("ユーザー作成失敗: " + response.code());
            }
        }
    }

    // POSTリクエスト(フォームデータ送信)
    public void postFormRequest() throws IOException {
        RequestBody formBody = new FormBody.Builder()
                .add("username", "testuser")
                .add("password", "secret123")
                .add("remember_me", "true")
                .build();

        Request request = new Request.Builder()
                .url("https://api.example.com/login")
                .post(formBody)
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                String responseBody = response.body().string();
                System.out.println("ログイン成功: " + responseBody);
            } else {
                System.out.println("ログイン失敗: " + response.code());
            }
        }
    }

    // PUTリクエスト(データ更新)
    public void putRequest() throws IOException {
        MediaType JSON = MediaType.get("application/json; charset=utf-8");
        String json = "{"
                + "\"name\": \"田中次郎\","
                + "\"email\": \"[email protected]\""
                + "}";

        RequestBody body = RequestBody.create(json, JSON);
        Request request = new Request.Builder()
                .url("https://api.example.com/users/123")
                .put(body)
                .addHeader("Authorization", "Bearer your-jwt-token")
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                System.out.println("ユーザー更新成功");
            } else {
                System.out.println("ユーザー更新失敗: " + response.code());
            }
        }
    }

    // DELETEリクエスト
    public void deleteRequest() throws IOException {
        Request request = new Request.Builder()
                .url("https://api.example.com/users/123")
                .delete()
                .addHeader("Authorization", "Bearer your-jwt-token")
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.code() == 204) {
                System.out.println("ユーザー削除成功");
            } else {
                System.out.println("ユーザー削除失敗: " + response.code());
            }
        }
    }

    // レスポンス詳細情報の取得
    public void detailedResponseInfo() throws IOException {
        Request request = new Request.Builder()
                .url("https://api.example.com/info")
                .build();

        try (Response response = client.newCall(request).execute()) {
            System.out.println("ステータスコード: " + response.code());
            System.out.println("メッセージ: " + response.message());
            System.out.println("プロトコル: " + response.protocol());
            System.out.println("URL: " + response.request().url());
            
            // ヘッダー一覧
            Headers headers = response.headers();
            for (int i = 0; i < headers.size(); i++) {
                System.out.println(headers.name(i) + ": " + headers.value(i));
            }
        }
    }
}

高度な設定とカスタマイズ(ヘッダー、認証、タイムアウト等)

import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.*;
import java.security.cert.CertificateException;

public class AdvancedOkHttpConfiguration {

    // カスタム設定付きOkHttpClientの作成
    public OkHttpClient createCustomClient() {
        // ログ機能の設定
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);

        return new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)      // 接続タイムアウト
                .writeTimeout(10, TimeUnit.SECONDS)        // 書き込みタイムアウト
                .readTimeout(30, TimeUnit.SECONDS)         // 読み込みタイムアウト
                .callTimeout(60, TimeUnit.SECONDS)         // 全体のタイムアウト
                .addInterceptor(logging)                   // ログ機能追加
                .addInterceptor(new AuthorizationInterceptor()) // 認証インターセプター
                .retryOnConnectionFailure(true)            // 接続失敗時のリトライ
                .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES)) // 接続プール設定
                .build();
    }

    // 認証用インターセプター
    public static class AuthorizationInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request original = chain.request();
            
            // 全てのリクエストにAuthorizationヘッダーを追加
            Request.Builder requestBuilder = original.newBuilder()
                    .addHeader("Authorization", "Bearer your-jwt-token")
                    .addHeader("User-Agent", "MyApp/1.0 (Android)")
                    .addHeader("Accept", "application/json");

            Request request = requestBuilder.build();
            return chain.proceed(request);
        }
    }

    // Basic認証の設定
    public void basicAuthExample() throws IOException {
        String credentials = Credentials.basic("username", "password");
        
        Request request = new Request.Builder()
                .url("https://api.example.com/private")
                .header("Authorization", credentials)
                .build();

        OkHttpClient client = new OkHttpClient();
        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                System.out.println("Basic認証成功: " + response.body().string());
            } else {
                System.out.println("Basic認証失敗: " + response.code());
            }
        }
    }

    // SSL証明書の無効化(開発時のみ)
    public OkHttpClient createUnsafeClient() {
        try {
            // 全ての証明書を信頼するTrustManager
            final TrustManager[] trustAllCerts = new TrustManager[] {
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
                            throws CertificateException {
                    }

                    @Override
                    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
                            throws CertificateException {
                    }

                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[]{};
                    }
                }
            };

            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            return new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0])
                    .hostnameVerifier(new HostnameVerifier() {
                        @Override
                        public boolean verify(String hostname, SSLSession session) {
                            return true;
                        }
                    })
                    .build();

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // プロキシ設定
    public void proxyConfiguration() throws IOException {
        java.net.Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, 
                new java.net.InetSocketAddress("proxy.example.com", 8080));

        OkHttpClient client = new OkHttpClient.Builder()
                .proxy(proxy)
                .build();

        Request request = new Request.Builder()
                .url("https://api.example.com/data")
                .build();

        try (Response response = client.newCall(request).execute()) {
            System.out.println("プロキシ経由レスポンス: " + response.body().string());
        }
    }

    // 認証付きプロキシ
    public void authenticatedProxyConfiguration() throws IOException {
        java.net.Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, 
                new java.net.InetSocketAddress("proxy.example.com", 8080));

        Authenticator proxyAuthenticator = new Authenticator() {
            @Override
            public Request authenticate(Route route, Response response) throws IOException {
                String credential = Credentials.basic("proxy-user", "proxy-pass");
                return response.request().newBuilder()
                        .header("Proxy-Authorization", credential)
                        .build();
            }
        };

        OkHttpClient client = new OkHttpClient.Builder()
                .proxy(proxy)
                .proxyAuthenticator(proxyAuthenticator)
                .build();

        Request request = new Request.Builder()
                .url("https://api.example.com/data")
                .build();

        try (Response response = client.newCall(request).execute()) {
            System.out.println("認証プロキシ経由レスポンス: " + response.body().string());
        }
    }

    // Cookie管理
    public void cookieManagement() throws IOException {
        CookieJar cookieJar = new CookieJar() {
            private final java.util.Map<String, java.util.List<Cookie>> cookieStore = new java.util.HashMap<>();

            @Override
            public void saveFromResponse(HttpUrl url, java.util.List<Cookie> cookies) {
                cookieStore.put(url.host(), cookies);
            }

            @Override
            public java.util.List<Cookie> loadForRequest(HttpUrl url) {
                java.util.List<Cookie> cookies = cookieStore.get(url.host());
                return cookies != null ? cookies : new java.util.ArrayList<>();
            }
        };

        OkHttpClient client = new OkHttpClient.Builder()
                .cookieJar(cookieJar)
                .build();

        // ログインしてCookieを取得
        MediaType JSON = MediaType.get("application/json");
        String loginJson = "{\"username\":\"user\",\"password\":\"pass\"}";
        RequestBody loginBody = RequestBody.create(loginJson, JSON);

        Request loginRequest = new Request.Builder()
                .url("https://api.example.com/login")
                .post(loginBody)
                .build();

        try (Response loginResponse = client.newCall(loginRequest).execute()) {
            if (loginResponse.isSuccessful()) {
                System.out.println("ログイン成功、Cookieが保存されました");
            }
        }

        // 後続のリクエストでCookieが自動的に送信される
        Request dataRequest = new Request.Builder()
                .url("https://api.example.com/protected")
                .build();

        try (Response dataResponse = client.newCall(dataRequest).execute()) {
            System.out.println("保護されたリソース: " + dataResponse.body().string());
        }
    }

    // カスタムヘッダーの一括設定
    public void customHeadersExample() throws IOException {
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request original = chain.request();
                        Request.Builder requestBuilder = original.newBuilder()
                                .addHeader("User-Agent", "MyApp/1.0 (Android)")
                                .addHeader("Accept-Language", "ja-JP,en-US;q=0.9")
                                .addHeader("X-API-Version", "v2")
                                .addHeader("X-Request-ID", java.util.UUID.randomUUID().toString());
                        
                        Request request = requestBuilder.build();
                        return chain.proceed(request);
                    }
                })
                .build();

        Request request = new Request.Builder()
                .url("https://api.example.com/data")
                .build();

        try (Response response = client.newCall(request).execute()) {
            System.out.println("カスタムヘッダー付きレスポンス: " + response.body().string());
        }
    }
}

エラーハンドリングとリトライ機能

import okhttp3.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class OkHttpErrorHandling {

    // 包括的なエラーハンドリング
    public void comprehensiveErrorHandling() {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://api.example.com/users")
                .build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // ネットワークエラー、タイムアウト、接続失敗等
                if (e instanceof java.net.SocketTimeoutException) {
                    System.out.println("タイムアウトエラー: " + e.getMessage());
                } else if (e instanceof java.net.ConnectException) {
                    System.out.println("接続エラー: " + e.getMessage());
                } else if (e instanceof java.net.UnknownHostException) {
                    System.out.println("ホスト名解決エラー: " + e.getMessage());
                } else {
                    System.out.println("ネットワークエラー: " + e.getMessage());
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    if (response.isSuccessful()) {
                        String responseBody = response.body().string();
                        System.out.println("成功: " + responseBody);
                    } else {
                        // HTTPエラーステータスの処理
                        handleHttpError(response);
                    }
                } finally {
                    response.close();
                }
            }
        });
    }

    // HTTPエラーステータスの詳細処理
    private void handleHttpError(Response response) throws IOException {
        int code = response.code();
        String errorBody = response.body() != null ? response.body().string() : "No error body";

        switch (code) {
            case 400:
                System.out.println("Bad Request: リクエストが無効です - " + errorBody);
                break;
            case 401:
                System.out.println("Unauthorized: 認証が必要です - " + errorBody);
                // トークンの再取得処理などをここに追加
                break;
            case 403:
                System.out.println("Forbidden: アクセス権限がありません - " + errorBody);
                break;
            case 404:
                System.out.println("Not Found: リソースが見つかりません - " + errorBody);
                break;
            case 429:
                System.out.println("Too Many Requests: レート制限です - " + errorBody);
                // Retry-Afterヘッダーをチェック
                String retryAfter = response.header("Retry-After");
                if (retryAfter != null) {
                    System.out.println("Retry after: " + retryAfter + " seconds");
                }
                break;
            case 500:
                System.out.println("Internal Server Error: サーバーエラーです - " + errorBody);
                break;
            case 502:
                System.out.println("Bad Gateway: ゲートウェイエラーです - " + errorBody);
                break;
            case 503:
                System.out.println("Service Unavailable: サービス利用不可です - " + errorBody);
                break;
            default:
                System.out.println("HTTP Error " + code + ": " + response.message() + " - " + errorBody);
                break;
        }
    }

    // リトライ機能付きインターセプター
    public static class RetryInterceptor implements Interceptor {
        private final int maxRetries;
        private final long retryDelayMillis;

        public RetryInterceptor(int maxRetries, long retryDelayMillis) {
            this.maxRetries = maxRetries;
            this.retryDelayMillis = retryDelayMillis;
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = null;
            IOException lastException = null;

            for (int retryCount = 0; retryCount <= maxRetries; retryCount++) {
                try {
                    if (response != null) {
                        response.close();
                    }
                    
                    response = chain.proceed(request);
                    
                    // 成功またはリトライ不要なステータス
                    if (response.isSuccessful() || !shouldRetry(response.code())) {
                        return response;
                    }
                    
                    // リトライが必要な場合
                    if (retryCount < maxRetries) {
                        System.out.println("リトライ " + (retryCount + 1) + "/" + maxRetries + 
                                " (ステータス: " + response.code() + ")");
                        
                        try {
                            Thread.sleep(retryDelayMillis * (retryCount + 1)); // 指数バックオフ
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            throw new IOException("Retry interrupted", e);
                        }
                    }
                    
                } catch (IOException e) {
                    lastException = e;
                    
                    if (retryCount < maxRetries) {
                        System.out.println("ネットワークエラーでリトライ " + (retryCount + 1) + "/" + maxRetries + 
                                ": " + e.getMessage());
                        
                        try {
                            Thread.sleep(retryDelayMillis * (retryCount + 1));
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                            throw new IOException("Retry interrupted", ie);
                        }
                    }
                }
            }

            if (lastException != null) {
                throw lastException;
            }
            
            return response;
        }

        private boolean shouldRetry(int statusCode) {
            // リトライ対象のステータスコード
            return statusCode == 408 || // Request Timeout
                   statusCode == 429 || // Too Many Requests
                   statusCode == 500 || // Internal Server Error
                   statusCode == 502 || // Bad Gateway
                   statusCode == 503 || // Service Unavailable
                   statusCode == 504;   // Gateway Timeout
        }
    }

    // リトライ機能付きクライアントの使用例
    public void retryClientExample() throws IOException {
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(new RetryInterceptor(3, 1000)) // 3回リトライ、1秒間隔
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();

        Request request = new Request.Builder()
                .url("https://api.example.com/unstable-endpoint")
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                System.out.println("リトライ後成功: " + response.body().string());
            } else {
                System.out.println("最終的に失敗: " + response.code());
            }
        }
    }

    // トークン自動更新インターセプター
    public static class TokenRefreshInterceptor implements Interceptor {
        private String accessToken;
        private final TokenRefreshService tokenService;

        public TokenRefreshInterceptor(String initialToken, TokenRefreshService tokenService) {
            this.accessToken = initialToken;
            this.tokenService = tokenService;
        }

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request original = chain.request();
            
            // 最初のリクエストにトークンを設定
            Request.Builder requestBuilder = original.newBuilder();
            if (accessToken != null) {
                requestBuilder.addHeader("Authorization", "Bearer " + accessToken);
            }
            
            Request request = requestBuilder.build();
            Response response = chain.proceed(request);
            
            // 401エラーの場合、トークンを更新して再試行
            if (response.code() == 401 && accessToken != null) {
                response.close();
                
                synchronized (this) {
                    // トークンを更新
                    String newToken = tokenService.refreshToken();
                    if (newToken != null) {
                        this.accessToken = newToken;
                        
                        // 新しいトークンで再試行
                        Request newRequest = original.newBuilder()
                                .addHeader("Authorization", "Bearer " + newToken)
                                .build();
                        
                        return chain.proceed(newRequest);
                    }
                }
            }
            
            return response;
        }
    }

    // トークン更新サービスのインターフェース
    public interface TokenRefreshService {
        String refreshToken() throws IOException;
    }

    // 例外の詳細な分類処理
    public void detailedExceptionHandling() {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://api.example.com/data")
                .build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 詳細な例外分類
                String errorMessage;
                String userMessage;

                if (e instanceof java.net.SocketTimeoutException) {
                    errorMessage = "Socket timeout: " + e.getMessage();
                    userMessage = "通信がタイムアウトしました。しばらく待ってから再試行してください。";
                } else if (e instanceof java.net.ConnectException) {
                    errorMessage = "Connection failed: " + e.getMessage();
                    userMessage = "サーバーに接続できません。ネットワーク接続を確認してください。";
                } else if (e instanceof java.net.UnknownHostException) {
                    errorMessage = "Unknown host: " + e.getMessage();
                    userMessage = "サーバーが見つかりません。インターネット接続を確認してください。";
                } else if (e instanceof javax.net.ssl.SSLException) {
                    errorMessage = "SSL error: " + e.getMessage();
                    userMessage = "セキュリティエラーが発生しました。";
                } else {
                    errorMessage = "Network error: " + e.getMessage();
                    userMessage = "ネットワークエラーが発生しました。";
                }

                // ログ出力
                System.out.println("Error: " + errorMessage);
                
                // ユーザーに表示するメッセージ
                showUserMessage(userMessage);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try {
                    if (response.isSuccessful()) {
                        String responseBody = response.body().string();
                        System.out.println("Success: " + responseBody);
                    } else {
                        handleHttpError(response);
                    }
                } finally {
                    response.close();
                }
            }
        });
    }

    private void showUserMessage(String message) {
        // Android UIスレッドでメッセージを表示する処理
        // 実際の実装では Handler や runOnUiThread を使用
        System.out.println("User Message: " + message);
    }
}

並行処理と非同期リクエスト

import okhttp3.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class OkHttpConcurrency {
    private final OkHttpClient client = new OkHttpClient();
    private final ExecutorService executorService = Executors.newFixedThreadPool(10);

    // 複数URLの並列取得
    public void fetchMultipleUrls() {
        List<String> urls = List.of(
                "https://api.example.com/users",
                "https://api.example.com/posts",
                "https://api.example.com/comments",
                "https://api.example.com/categories"
        );

        List<CompletableFuture<ApiResponse>> futures = new ArrayList<>();

        for (String url : urls) {
            CompletableFuture<ApiResponse> future = CompletableFuture.supplyAsync(() -> {
                try {
                    return fetchSingleUrl(url);
                } catch (IOException e) {
                    return new ApiResponse(url, false, 0, e.getMessage(), null);
                }
            }, executorService);
            
            futures.add(future);
        }

        // すべてのリクエストの完了を待機
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

        allFutures.thenRun(() -> {
            System.out.println("すべてのリクエストが完了しました");
            
            for (CompletableFuture<ApiResponse> future : futures) {
                try {
                    ApiResponse response = future.get();
                    if (response.isSuccess()) {
                        System.out.println("成功 " + response.getUrl() + ": " + 
                                response.getStatusCode());
                    } else {
                        System.out.println("失敗 " + response.getUrl() + ": " + 
                                response.getError());
                    }
                } catch (InterruptedException | ExecutionException e) {
                    System.out.println("Future取得エラー: " + e.getMessage());
                }
            }
        }).join(); // メインスレッドで完了を待機
    }

    // 単一URLの同期取得
    private ApiResponse fetchSingleUrl(String url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .build();

        long startTime = System.currentTimeMillis();
        
        try (Response response = client.newCall(request).execute()) {
            long responseTime = System.currentTimeMillis() - startTime;
            String responseBody = response.body() != null ? response.body().string() : "";
            
            return new ApiResponse(
                    url,
                    response.isSuccessful(),
                    response.code(),
                    null,
                    responseBody
            );
        } catch (IOException e) {
            long responseTime = System.currentTimeMillis() - startTime;
            return new ApiResponse(url, false, 0, e.getMessage(), null);
        }
    }

    // セマフォによる同時接続数制御
    public void controlledConcurrentRequests() {
        List<String> urls = generateUrlList(20); // 20個のURL
        Semaphore semaphore = new Semaphore(5); // 最大5同時接続

        List<CompletableFuture<ApiResponse>> futures = new ArrayList<>();

        for (String url : urls) {
            CompletableFuture<ApiResponse> future = CompletableFuture.supplyAsync(() -> {
                try {
                    semaphore.acquire(); // セマフォを取得
                    return fetchSingleUrl(url);
                } catch (IOException | InterruptedException e) {
                    return new ApiResponse(url, false, 0, e.getMessage(), null);
                } finally {
                    semaphore.release(); // セマフォを解放
                }
            }, executorService);
            
            futures.add(future);
        }

        // 結果の処理
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                .thenRun(() -> {
                    int successCount = 0;
                    for (CompletableFuture<ApiResponse> future : futures) {
                        try {
                            ApiResponse response = future.get();
                            if (response.isSuccess()) {
                                successCount++;
                            }
                        } catch (InterruptedException | ExecutionException e) {
                            System.out.println("エラー: " + e.getMessage());
                        }
                    }
                    System.out.println("成功したリクエスト: " + successCount + "/" + urls.size());
                }).join();
    }

    // ページネーション対応の段階的データ取得
    public void fetchAllPaginatedData(String baseUrl) {
        List<Object> allData = new ArrayList<>();
        int page = 1;
        boolean hasMore = true;

        while (hasMore) {
            try {
                HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder();
                urlBuilder.addQueryParameter("page", String.valueOf(page));
                urlBuilder.addQueryParameter("per_page", "100");

                Request request = new Request.Builder()
                        .url(urlBuilder.build())
                        .addHeader("Authorization", "Bearer your-token")
                        .build();

                try (Response response = client.newCall(request).execute()) {
                    if (response.isSuccessful()) {
                        String responseBody = response.body().string();
                        
                        // JSONパースは実際の実装ではGsonやJacksonを使用
                        // ここではシンプルな例として文字列処理
                        if (responseBody.contains("\"items\":[]") || responseBody.equals("[]")) {
                            hasMore = false;
                        } else {
                            // データを追加(実際の実装ではJSONパース結果)
                            System.out.println("ページ " + page + " 取得完了");
                            page++;
                            
                            // API負荷軽減のための待機
                            Thread.sleep(100);
                        }
                    } else {
                        System.out.println("ページ " + page + " でエラー: " + response.code());
                        hasMore = false;
                    }
                }
            } catch (IOException | InterruptedException e) {
                System.out.println("ページ " + page + " で例外: " + e.getMessage());
                hasMore = false;
            }
        }

        System.out.println("総取得ページ数: " + (page - 1));
    }

    // バッチ処理による効率的なリクエスト
    public void batchProcessRequests(List<String> items) {
        int batchSize = 10;
        List<List<String>> batches = createBatches(items, batchSize);

        for (int i = 0; i < batches.size(); i++) {
            List<String> batch = batches.get(i);
            System.out.println("バッチ " + (i + 1) + " 処理中: " + batch.size() + "項目");

            List<CompletableFuture<ApiResponse>> batchFutures = new ArrayList<>();

            for (String item : batch) {
                String url = "https://api.example.com/items/" + item;
                CompletableFuture<ApiResponse> future = CompletableFuture.supplyAsync(() -> {
                    try {
                        return fetchSingleUrl(url);
                    } catch (IOException e) {
                        return new ApiResponse(url, false, 0, e.getMessage(), null);
                    }
                }, executorService);
                
                batchFutures.add(future);
            }

            // バッチ内のすべてのリクエスト完了を待機
            CompletableFuture.allOf(batchFutures.toArray(new CompletableFuture[0])).join();

            // バッチ間の待機
            if (i < batches.size() - 1) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }

    // 依存関係のあるリクエストの順次処理
    public void dependentRequestsPipeline() {
        try {
            // Step 1: ユーザー情報取得
            Request userRequest = new Request.Builder()
                    .url("https://api.example.com/user/profile")
                    .addHeader("Authorization", "Bearer your-token")
                    .build();

            String userId;
            try (Response userResponse = client.newCall(userRequest).execute()) {
                if (!userResponse.isSuccessful()) {
                    System.out.println("ユーザー情報取得失敗: " + userResponse.code());
                    return;
                }
                
                String userBody = userResponse.body().string();
                // JSONパース(実際の実装ではGsonなど使用)
                userId = extractUserIdFromJson(userBody);
            }

            // Step 2: ユーザーの投稿を並列取得
            List<String> categories = List.of("tech", "personal", "work");
            List<CompletableFuture<ApiResponse>> postsFutures = new ArrayList<>();

            for (String category : categories) {
                String url = "https://api.example.com/users/" + userId + "/posts/" + category;
                CompletableFuture<ApiResponse> future = CompletableFuture.supplyAsync(() -> {
                    try {
                        return fetchSingleUrl(url);
                    } catch (IOException e) {
                        return new ApiResponse(url, false, 0, e.getMessage(), null);
                    }
                }, executorService);
                
                postsFutures.add(future);
            }

            // Step 3: すべての投稿データの取得完了を待機
            CompletableFuture.allOf(postsFutures.toArray(new CompletableFuture[0]))
                    .thenRun(() -> {
                        System.out.println("すべてのカテゴリの投稿データ取得完了");
                        
                        for (int i = 0; i < categories.size(); i++) {
                            try {
                                ApiResponse response = postsFutures.get(i).get();
                                System.out.println(categories.get(i) + " カテゴリ: " + 
                                        (response.isSuccess() ? "成功" : "失敗"));
                            } catch (InterruptedException | ExecutionException e) {
                                System.out.println("エラー: " + e.getMessage());
                            }
                        }
                    }).join();

        } catch (IOException e) {
            System.out.println("依存リクエストパイプラインでエラー: " + e.getMessage());
        }
    }

    // ヘルパーメソッド
    private List<String> generateUrlList(int count) {
        List<String> urls = new ArrayList<>();
        for (int i = 1; i <= count; i++) {
            urls.add("https://api.example.com/items/" + i);
        }
        return urls;
    }

    private <T> List<List<T>> createBatches(List<T> items, int batchSize) {
        List<List<T>> batches = new ArrayList<>();
        for (int i = 0; i < items.size(); i += batchSize) {
            int end = Math.min(i + batchSize, items.size());
            batches.add(items.subList(i, end));
        }
        return batches;
    }

    private String extractUserIdFromJson(String json) {
        // 実際の実装ではJSONライブラリを使用
        // ここでは簡単な例として固定値を返す
        return "123";
    }

    // APIレスポンスを表すクラス
    public static class ApiResponse {
        private final String url;
        private final boolean success;
        private final int statusCode;
        private final String error;
        private final String data;

        public ApiResponse(String url, boolean success, int statusCode, String error, String data) {
            this.url = url;
            this.success = success;
            this.statusCode = statusCode;
            this.error = error;
            this.data = data;
        }

        // ゲッターメソッド
        public String getUrl() { return url; }
        public boolean isSuccess() { return success; }
        public int getStatusCode() { return statusCode; }
        public String getError() { return error; }
        public String getData() { return data; }
    }

    // リソースのクリーンアップ
    public void cleanup() {
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

フレームワーク統合と実用例

// Android統合例(Retrofit + OkHttp)
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.*;
import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;

public class AndroidIntegrationExample {

    // Retrofit サービスインターフェース
    public interface ApiService {
        @GET("users")
        Call<List<User>> getUsers(@Query("page") int page);

        @POST("users")
        Call<User> createUser(@Body CreateUserRequest request);

        @PUT("users/{id}")
        Call<User> updateUser(@Path("id") String id, @Body UpdateUserRequest request);

        @DELETE("users/{id}")
        Call<ResponseBody> deleteUser(@Path("id") String id);

        @Multipart
        @POST("upload")
        Call<UploadResponse> uploadFile(
                @Part MultipartBody.Part file,
                @Part("description") RequestBody description
        );
    }

    // Android統合用のAPIクライアント
    public class AndroidApiClient {
        private final ApiService apiService;
        private final OkHttpClient okHttpClient;

        public AndroidApiClient(String baseUrl, String authToken) {
            // ログインターセプターの設定
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);

            // 認証インターセプターの設定
            Interceptor authInterceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request original = chain.request();
                    Request.Builder requestBuilder = original.newBuilder()
                            .addHeader("Authorization", "Bearer " + authToken)
                            .addHeader("User-Agent", "MyAndroidApp/1.0");
                    Request request = requestBuilder.build();
                    return chain.proceed(request);
                }
            };

            // OkHttpClientの設定
            this.okHttpClient = new OkHttpClient.Builder()
                    .addInterceptor(authInterceptor)
                    .addInterceptor(logging)
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
                    .retryOnConnectionFailure(true)
                    .build();

            // Retrofitの設定
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(okHttpClient)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

            this.apiService = retrofit.create(ApiService.class);
        }

        // 非同期でユーザー一覧を取得
        public void fetchUsers(int page, ApiCallback<List<User>> callback) {
            Call<List<User>> call = apiService.getUsers(page);
            call.enqueue(new retrofit2.Callback<List<User>>() {
                @Override
                public void onResponse(Call<List<User>> call, retrofit2.Response<List<User>> response) {
                    if (response.isSuccessful() && response.body() != null) {
                        callback.onSuccess(response.body());
                    } else {
                        callback.onError("HTTP " + response.code() + ": " + response.message());
                    }
                }

                @Override
                public void onFailure(Call<List<User>> call, Throwable t) {
                    callback.onError("Network error: " + t.getMessage());
                }
            });
        }

        // ファイルアップロード
        public void uploadFile(java.io.File file, String description, ApiCallback<UploadResponse> callback) {
            RequestBody requestFile = RequestBody.create(file, MediaType.parse("multipart/form-data"));
            MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
            RequestBody descriptionBody = RequestBody.create(description, MediaType.parse("text/plain"));

            Call<UploadResponse> call = apiService.uploadFile(body, descriptionBody);
            call.enqueue(new retrofit2.Callback<UploadResponse>() {
                @Override
                public void onResponse(Call<UploadResponse> call, retrofit2.Response<UploadResponse> response) {
                    if (response.isSuccessful() && response.body() != null) {
                        callback.onSuccess(response.body());
                    } else {
                        callback.onError("Upload failed: " + response.code());
                    }
                }

                @Override
                public void onFailure(Call<UploadResponse> call, Throwable t) {
                    callback.onError("Upload error: " + t.getMessage());
                }
            });
        }

        public void shutdown() {
            if (okHttpClient != null) {
                okHttpClient.dispatcher().executorService().shutdown();
                okHttpClient.connectionPool().evictAll();
            }
        }
    }

    // WebSocket統合例
    public class WebSocketManager {
        private OkHttpClient client;
        private WebSocket webSocket;
        private WebSocketListener listener;

        public WebSocketManager() {
            this.client = new OkHttpClient.Builder()
                    .readTimeout(0, TimeUnit.MILLISECONDS)
                    .build();

            this.listener = new WebSocketListener() {
                @Override
                public void onOpen(WebSocket webSocket, Response response) {
                    System.out.println("WebSocket接続が確立されました");
                    // 接続成功時の処理
                }

                @Override
                public void onMessage(WebSocket webSocket, String text) {
                    System.out.println("受信メッセージ: " + text);
                    // メッセージ受信時の処理
                    handleWebSocketMessage(text);
                }

                @Override
                public void onClosing(WebSocket webSocket, int code, String reason) {
                    webSocket.close(1000, null);
                    System.out.println("WebSocket接続を閉じています: " + reason);
                }

                @Override
                public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                    System.out.println("WebSocketエラー: " + t.getMessage());
                    // エラー時の再接続処理など
                }
            };
        }

        public void connect(String url) {
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("Authorization", "Bearer your-token")
                    .build();

            this.webSocket = client.newWebSocket(request, listener);
        }

        public void sendMessage(String message) {
            if (webSocket != null) {
                webSocket.send(message);
            }
        }

        public void disconnect() {
            if (webSocket != null) {
                webSocket.close(1000, "Goodbye!");
            }
        }

        private void handleWebSocketMessage(String message) {
            // メッセージのパースと処理
            // 実際の実装ではJSONパースなど
            System.out.println("処理中: " + message);
        }

        public void shutdown() {
            disconnect();
            client.dispatcher().executorService().shutdown();
        }
    }

    // キャッシュ機能の実装
    public class CacheManager {
        private static final int CACHE_SIZE = 10 * 1024 * 1024; // 10MB

        public OkHttpClient createCachedClient(android.content.Context context) {
            java.io.File cacheDirectory = new java.io.File(context.getCacheDir(), "http-cache");
            Cache cache = new Cache(cacheDirectory, CACHE_SIZE);

            return new OkHttpClient.Builder()
                    .cache(cache)
                    .addNetworkInterceptor(new CacheControlInterceptor())
                    .build();
        }

        // キャッシュ制御インターセプター
        private static class CacheControlInterceptor implements Interceptor {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request = chain.request();

                // ネットワーク接続の確認
                if (!isNetworkAvailable()) {
                    request = request.newBuilder()
                            .cacheControl(CacheControl.FORCE_CACHE)
                            .build();
                }

                Response response = chain.proceed(request);

                if (isNetworkAvailable()) {
                    // ネットワーク利用可能時: 1分間キャッシュ
                    response = response.newBuilder()
                            .header("Cache-Control", "public, max-age=60")
                            .build();
                } else {
                    // オフライン時: 7日間キャッシュを利用
                    response = response.newBuilder()
                            .header("Cache-Control", "public, only-if-cached, max-stale=604800")
                            .build();
                }

                return response;
            }

            private boolean isNetworkAvailable() {
                // 実際の実装では ConnectivityManager を使用
                return true;
            }
        }
    }

    // コールバックインターフェース
    public interface ApiCallback<T> {
        void onSuccess(T result);
        void onError(String error);
    }

    // データクラス
    public static class User {
        public String id;
        public String name;
        public String email;
    }

    public static class CreateUserRequest {
        public String name;
        public String email;
    }

    public static class UpdateUserRequest {
        public String name;
        public String email;
    }

    public static class UploadResponse {
        public String fileId;
        public String url;
    }

    // 使用例
    public void example() {
        // APIクライアントの使用
        AndroidApiClient apiClient = new AndroidApiClient(
                "https://api.example.com/v1/",
                "your-auth-token"
        );

        apiClient.fetchUsers(1, new ApiCallback<List<User>>() {
            @Override
            public void onSuccess(List<User> users) {
                System.out.println("ユーザー一覧取得成功: " + users.size() + "件");
            }

            @Override
            public void onError(String error) {
                System.out.println("エラー: " + error);
            }
        });

        // WebSocketの使用
        WebSocketManager wsManager = new WebSocketManager();
        wsManager.connect("wss://api.example.com/websocket");
        wsManager.sendMessage("{\"type\":\"subscribe\",\"channel\":\"updates\"}");

        // クリーンアップ
        apiClient.shutdown();
        wsManager.shutdown();
    }
}