Spanish Configurar el readTimeout de una conexión OkHttp

Jose Cuevas

Member
Licensed User
Longtime User
Hola amigos, estoy consumiendo un Web Service tipo REST, usando una conexión HttpJob y configurando el TimeOut a 6 segundos con este comando XJob.GetRequest.Timeout = 6000, este timeout es muy importante, porque es una App de uso intenso y si el WebService tarda más de 6 segundos, debe pasar a otro proceso.

Pero me han dicho que el comando GetRequest.Timeout es solo para el Timeout de conexión y que OkHttp tiene un Timeout para conexión (connectTimeout) y otro para lectura (readTimeout) (https://www.baeldung.com/okhttp-timeouts). Entonces cuando la conexión dura más de 6 segundos, el Timeout de conexión se dispara y todo funciona bien.

El problema viene cuando la conexión dura menos de 6 segundos, pero la respuesta del WebSevice puede durar hasta 30 segundos, entonces el Timeout de respuesta lo controla el readTimeout. Encontré este código en Java, pero no se programar en Java y tampoco cómo implementar este código en B4A:

B4X:
@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);
}

¿Existe alguna forma de configurar el readTimeout en B4A nativo?

Les agradecería mucho cualquier ayuda que me puedan brindar, ya que estoy por encima de mi deadline y mi cliente empieza a enojarse.

De antemano muchas gracias.
 

TILogistic

Expert
Licensed User
Longtime User
he visto tus post en el foro ingles y veo que ya tienes la solución de llamadas a java, porque no utilizas ese ejemplo para hacer lo que quieres.


B4X:
Sub RetryOff
 
    Dim jo As JavaObject = HttpUtils2Service.hc
    Dim builder As JavaObject = jo.RunMethod("sharedInit", Array("hc"))
    builder.RunMethod("retryOnConnectionFailure", Array(False))
    jo.SetField("client", builder.RunMethod("build", Null))

End Sub

el Wrapper de OkHttp lo esta configurando a 30000.

https://github.com/AnywhereSoftware...esoftware/b4h/okhttp/OkHttpClientWrapper.java

Java:
    @Hide
    public OkHttpClient.Builder sharedInit(String EventName) {
        okhttp3.OkHttpClient.Builder builder = new okhttp3.OkHttpClient.Builder();
        this.eventName = EventName.toLowerCase(BA.cul);
        setTimeout(builder, 30000);
        CookieManager cm = new CookieManager();
        cm.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
        builder.cookieJar(new JavaNetCookieJar(cm));
        return builder;
    }
    public boolean IsInitialized() {
        return client != null;
    }
    static void setTimeout(OkHttpClient.Builder builder, int TimeoutMs) {
        builder.connectTimeout(TimeoutMs, TimeUnit.MILLISECONDS);
        builder.writeTimeout(TimeoutMs, TimeUnit.MILLISECONDS);
        builder.readTimeout(TimeoutMs, TimeUnit.MILLISECONDS);
    }
 

Jose Cuevas

Member
Licensed User
Longtime User
he visto tus post en el foro ingles y veo que ya tienes la solución de llamadas a java, porque no utilizas ese ejemplo para hacer lo que quieres.


B4X:
Sub RetryOff
 
    Dim jo As JavaObject = HttpUtils2Service.hc
    Dim builder As JavaObject = jo.RunMethod("sharedInit", Array("hc"))
    builder.RunMethod("retryOnConnectionFailure", Array(False))
    jo.SetField("client", builder.RunMethod("build", Null))

End Sub

el Wrapper de OkHttp lo esta configurando a 30000.

https://github.com/AnywhereSoftware...esoftware/b4h/okhttp/OkHttpClientWrapper.java

Java:
    @Hide
    public OkHttpClient.Builder sharedInit(String EventName) {
        okhttp3.OkHttpClient.Builder builder = new okhttp3.OkHttpClient.Builder();
        this.eventName = EventName.toLowerCase(BA.cul);
        setTimeout(builder, 30000);
        CookieManager cm = new CookieManager();
        cm.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
        builder.cookieJar(new JavaNetCookieJar(cm));
        return builder;
    }
    public boolean IsInitialized() {
        return client != null;
    }
    static void setTimeout(OkHttpClient.Builder builder, int TimeoutMs) {
        builder.connectTimeout(TimeoutMs, TimeUnit.MILLISECONDS);
        builder.writeTimeout(TimeoutMs, TimeUnit.MILLISECONDS);
        builder.readTimeout(TimeoutMs, TimeUnit.MILLISECONDS);
    }
Que tal Omar, muchas gracias por tu respuesta. De hecho estoy utilizando esa base y he probado con este código:

B4X:
Sub ReadTimeout
    
    Dim jo As JavaObject = HttpUtils2Service.hc
    Dim builder As JavaObject = jo.RunMethod("sharedInit", Array("hc"))
    builder.RunMethod("readTimeout", Array(6000, "TimeUnit.MILLISECONDS"))
    jo.SetField("client", builder.RunMethod("build", Null))
    
End Sub

pero me está regresando este error: java.lang.RuntimeException: Method: readTimeout not matched.

Es claro que uno de los parámetros no es el correcto y creo que es segundo "TimeUnit.MILLISECONDS" porque probé con builder.RunMethod("readTimeout", Array(Null, Null)) y me dijo que el parámetro 1 debe ser long

java.lang.IllegalArgumentException: method okhttp3.OkHttpClient$Builder.readTimeout argument 1 has type long, got null

Probé también el segundo parámetro con valores como 0, 1, 2, etc. y en eso estoy batallando.
 

TILogistic

Expert
Licensed User
Longtime User
perdon TimeUnit.MILLISECONDS es de java util TimeUnits.

voy a revisar un ejemplo que hice un tiempo atrás.

saludos.
 

Jose Cuevas

Member
Licensed User
Longtime User
perdon TimeUnit.MILLISECONDS es de java util TimeUnits.

voy a revisar un ejemplo que hice un tiempo atrás.

saludos.
Exacto, tu sugerencia funcionó muy bien, ahora me da error el TimeUnit:

java.lang.IllegalArgumentException: No enum constant java.util.concurrent.TimeUnit.TimeUnit.MILLISECONDS

Muchas gracias por tu ayuda.
 

Jose Cuevas

Member
Licensed User
Longtime User
Creo que lo tengo, este es el código y no me dá ningún error, solo me hace falta probarlo en un área en donde la señal de Internet sea mala para corroborar que funcionan estos TimeOuts.

B4X:
Sub ReadTimeout
  
    Dim TimeOut As Long = 6000
    Dim jo As JavaObject = HttpUtils2Service.hc
    Dim builder As JavaObject = jo.RunMethod("sharedInit", Array("hc"))
  
    Dim TimeUnit As JavaObject
    TimeUnit.InitializeStatic("java.util.concurrent.TimeUnit")
    Dim Milliseconds As JavaObject = TimeUnit.GetField("MILLISECONDS")
  
    builder.RunMethod("readTimeout", Array(TimeOut, Milliseconds))
    builder.RunMethod("callTimeout", Array(TimeOut, Milliseconds))
    jo.SetField("client", builder.RunMethod("build", Null))
  
End Sub

Ahora solo hace falta saber en que línea debo hacer la llamada a este Sub, yo creo que después del XJob.GetRequest.Timeout = 6000

Muchas gracias Omar, saludos.
 
Last edited:

Jose Cuevas

Member
Licensed User
Longtime User
Este es el código final:

B4X:
Sub OkHttpTimeout
    
    Dim connTimeOut As Long = 5000
    Dim readTimeOut As Long = 2000
    Dim callTimeOut As Long = 2000
    Dim jo As JavaObject = HttpUtils2Service.hc
    Dim builder As JavaObject = jo.RunMethod("sharedInit", Array("hc"))
    
    Dim TimeUnit As JavaObject
    TimeUnit.InitializeStatic("java.util.concurrent.TimeUnit")
    Dim Milliseconds As JavaObject = TimeUnit.GetField("MILLISECONDS")
    
    builder.RunMethod("connectTimeout", Array(connTimeOut, Milliseconds))
    builder.RunMethod("readTimeout", Array(readTimeOut, Milliseconds))
    builder.RunMethod("callTimeout", Array(callTimeOut, Milliseconds))
    jo.SetField("client", builder.RunMethod("build", Null))
    
End Sub

Lo probé y si hace la diferenciación de tipo de TimeOut:

Cuando es por conexión regresa: java.io.InterruptedIOException: timeout
Y cuando es por read o call, regresa: okhttp3.internal.http2.StreamResetException: stream was reset: CANCEL

Ya no uso el GetRequest.Timeout, en lugar de esta línea, llamo a OkHttpTimeout

Muchas gracias Omar fuiste de gran ayuda.
 
Top