OkHttp
Android開発者Square社製のJava/Kotlin向け高性能HTTPクライアント。HTTP/2サポート、接続プーリング、GZIP圧縮、レスポンスキャッシング、ネットワーク障害からの自動復旧機能を内蔵。Android及びJVMプラットフォームで最適化。
GitHub概要
square/okhttp
Square’s meticulous HTTP client for the JVM, Android, and GraalVM.
トピックス
スター履歴
ライブラリ
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();
}
}