1. 概要

このクイックチュートリアルでは、OkHttpクライアントに設定できるさまざまなタイプのタイムアウトに焦点を当てます。

OkHttpライブラリのより一般的な概要については、紹介OkHttpガイドを確認してください。

2. 接続タイムアウト

接続タイムアウトは、クライアントがターゲットホストとの接続を確立する必要がある期間を定義します。

デフォルトでは、OkHttpClientの場合、このタイムアウトは10秒に設定されています

ただし、 OkHttpClient.Builder#connectTimeoutメソッドを使用してその値を簡単に変更できます。 ゼロの値は、タイムアウトがまったくないことを意味します。

次に、カスタム接続タイムアウトを使用してOkHttpClientをビルドして使用する方法を見てみましょう。

@Test
public void whenConnectTimeoutExceeded_thenSocketTimeoutException() {
    OkHttpClient client = new OkHttpClient.Builder()
      .connectTimeout(10, TimeUnit.MILLISECONDS)
      .build();

    Request request = new Request.Builder()
      .url("http://203.0.113.1") // non routable address
      .build();

    Throwable thrown = catchThrowable(() -> client.newCall(request).execute());

    assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}

上記の例は、接続の試行が構成されたタイムアウトを超えたときに、クライアントがSocketTimeoutExceptionをスローすることを示しています。

3. 読み取りタイムアウト

読み取りタイムアウトは、クライアントとターゲットホスト間の接続が正常に確立された瞬間から適用されます。

これは、サーバーの応答を待機しているときに2つのデータパケット間の非アクティブの最大時間を定義します

デフォルトのタイムアウトである10秒は、 OkHttpClient.Builder#readTimeoutを使用して変更できます。 接続タイムアウトの場合と同様に、ゼロ値はタイムアウトがないことを示します。

実際にカスタム読み取りタイムアウトを構成する方法を見てみましょう。

@Test
public void whenReadTimeoutExceeded_thenSocketTimeoutException() {
    OkHttpClient client = new OkHttpClient.Builder()
      .readTimeout(10, TimeUnit.MILLISECONDS)
      .build();

    Request request = new Request.Builder()
      .url("https://httpbin.org/delay/2") // 2-second response time
      .build();

    Throwable thrown = catchThrowable(() -> client.newCall(request).execute());

    assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}

ご覧のとおり、サーバーは定義されたタイムアウト500ミリ秒以内に応答を返しません。 その結果、OkHttpClientSocketTimeoutException。をスローします。

4. 書き込みタイムアウト

書き込みタイムアウトは、サーバーに要求を送信するときに2つのデータパケット間の非アクティブの最大時間を定義します。

同様に、接続と読み取りのタイムアウトについては、 OkHttpClient.Builder#writeTimeoutを使用してデフォルト値の10秒をオーバーライドできます。 慣例として、ゼロ値はタイムアウトがまったくないことを意味します。

次の例では、書き込みタイムアウトを10ミリ秒と非常に短く設定し、1MBのコンテンツをサーバーに送信します。

@Test
public void whenWriteTimeoutExceeded_thenSocketTimeoutException() {
    OkHttpClient client = new OkHttpClient.Builder()
      .writeTimeout(10, TimeUnit.MILLISECONDS)
      .build();

    Request request = new Request.Builder()
      .url("https://httpbin.org/delay/2")
      .post(RequestBody.create(MediaType.parse("text/plain"), create1MBString()))
      .build();

    Throwable thrown = catchThrowable(() -> client.newCall(request).execute());

    assertThat(thrown).isInstanceOf(SocketTimeoutException.class);
}

ご覧のとおり、ペイロードが大きいため、クライアントは定義されたタイムアウト内にサーバーにリクエスト本文を送信できません。 その結果、OkHttpClientSocketTimeoutExceptionをスローします。

5. 通話タイムアウト

呼び出しタイムアウトは、すでに説明した接続、読み取り、書き込みのタイムアウトとは少し異なります。

完全なHTTP呼び出しの時間制限を定義します。 これには、DNSの解決、接続、要求本文の書き込み、サーバー処理、および応答本文の読み取りが含まれます。

他のタイムアウトとは異なり、のデフォルト値はゼロに設定されています。これはタイムアウトがないことを意味します。 ただし、もちろん、 OkHttpClient.Builder#callTimeoutメソッドを使用してカスタム値を構成できます。

実際の使用例を見てみましょう。

@Test
public void whenCallTimeoutExceeded_thenInterruptedIOException() {
    OkHttpClient client = new OkHttpClient.Builder()
      .callTimeout(1, TimeUnit.SECONDS)
      .build();

    Request request = new Request.Builder()
      .url("https://httpbin.org/delay/2")
      .build();

    Throwable thrown = catchThrowable(() -> client.newCall(request).execute());

    assertThat(thrown).isInstanceOf(InterruptedIOException.class);
}

ご覧のとおり、呼び出しタイムアウトを超え、OkHttpClientInterruptedIOExceptionをスローします。

6. リクエストごとのタイムアウト

単一のOkHttpClientインスタンスを作成し、それをアプリケーション全体のすべてのHTTP呼び出しに再利用することをお勧めします。

ただし、特定のリクエストが他のすべてのリクエストよりも時間がかかることがわかっている場合があります。 この状況では、特定の呼び出しに対してのみ特定のタイムアウトを延長する必要があります。

このような場合、 OkHttpClient#newBuilderメソッドを使用できます。 これにより、同じ設定を共有する新しいクライアントが構築されます。 その後、ビルダーメソッドを使用して、必要に応じてタイムアウト設定を調整できます。

これを実際に行う方法を見てみましょう。

@Test
public void whenPerRequestTimeoutExtended_thenResponseSuccess() throws IOException {
    OkHttpClient defaultClient = new OkHttpClient.Builder()
      .readTimeout(1, TimeUnit.SECONDS)
      .build();

    Request request = new Request.Builder()
      .url("https://httpbin.org/delay/2")
      .build();

    Throwable thrown = catchThrowable(() -> defaultClient.newCall(request).execute());

    assertThat(thrown).isInstanceOf(InterruptedIOException.class);

    OkHttpClient extendedTimeoutClient = defaultClient.newBuilder()
      .readTimeout(5, TimeUnit.SECONDS)
      .build();

    Response response = extendedTimeoutClient.newCall(request).execute();
    assertThat(response.code()).isEqualTo(200);
}

ご覧のとおり、読み取りタイムアウトを超えたため、defaultClientはHTTP呼び出しを完了できませんでした。

そのため、 extendedTimeoutClientを作成し、でタイムアウト値を調整して、リクエストを正常に実行しました。

7. 概要

この記事では、OkHttpClientに構成できるさまざまなタイムアウトについて説明しました。

また、HTTP呼び出し中に接続、読み取り、および書き込みのタイムアウトがいつ適用されるかについても簡単に説明しました。

さらに、単一のリクエストに対してのみ特定のタイムアウト値を変更することがいかに簡単であるかを示しました

いつものように、すべてのコード例はGitHubから入手できます。