android - programacion - Devolución de llamada de respuesta Okhttp en el hilo principal
manual de programacion android pdf (4)
Creé una clase de ayuda para manejar todas mis llamadas http en mi aplicación. Es un envoltorio de singleton simple para okhttp que se ve así (he omitido algunas partes sin importancia):
public class HttpUtil {
private OkHttpClient client;
private Request.Builder builder;
...
public void get(String url, HttpCallback cb) {
call("GET", url, cb);
}
public void post(String url, HttpCallback cb) {
call("POST", url, cb);
}
private void call(String method, String url, final HttpCallback cb) {
Request request = builder.url(url).method(method, method.equals("GET") ? null : new RequestBody() {
// don''t care much about request body
@Override
public MediaType contentType() {
return null;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
}
}).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Request request, Throwable throwable) {
cb.onFailure(null, throwable);
}
@Override
public void onResponse(Response response) throws IOException {
if (!response.isSuccessful()) {
cb.onFailure(response, null);
return;
}
cb.onSuccess(response);
}
});
}
public interface HttpCallback {
/**
* called when the server response was not 2xx or when an exception was thrown in the process
* @param response - in case of server error (4xx, 5xx) this contains the server response
* in case of IO exception this is null
* @param throwable - contains the exception. in case of server error (4xx, 5xx) this is null
*/
public void onFailure(Response response, Throwable throwable);
/**
* contains the server response
* @param response
*/
public void onSuccess(Response response);
}
}
Luego, en mi actividad principal, uso esta clase de ayudante:
HttpUtil.get(url, new HttpUtil.HttpCallback() {
@Override
public void onFailure(Response response, Throwable throwable) {
// handle failure
}
@Override
public void onSuccess(Response response) {
// <-------- Do some view manipulation here
}
});
onSuccess
arroja una excepción cuando se ejecuta el código:
android.view.ViewRootImpl $ CalledFromWrongThreadException: solo el subproceso original que creó una jerarquía de vista puede tocar sus vistas.
Según entiendo, las devoluciones de llamada Okhttp se ejecutan en el hilo principal, entonces ¿por qué me sale este error?
** Como nota al margen, he creado la interfaz HttpCallback
para ajustar la clase Callback
de Okhttp porque quería cambiar el comportamiento de onResponse
y onFailure
para poder unir la lógica del manejo de respuestas fallidas debido a excepción de onResponse
/ onFailure
y respuestas fallidas debido a problemas del servidor.
Gracias.
Según entiendo, las devoluciones de llamada Okhttp se ejecutan en el hilo principal, entonces ¿por qué me sale este error?
Esto no es verdad. Las devoluciones de llamada se ejecutan en un hilo de fondo. Si desea procesar algo inmediatamente en la interfaz de usuario, deberá publicarlo en el hilo principal.
Como ya tiene un contenedor alrededor de la devolución de llamada, puede hacerlo internamente en su ayudante para que todos los métodos HttpCallback
se invoquen en el hilo principal para su comodidad.
Como sugirió Jake Wharton, tuve que ejecutar de forma explícita las devoluciones de llamada en el hilo principal.
Así que envolví las llamadas a las devoluciones de llamada con Runnable
así:
private void call(String method, String url, final HttpCallback cb) {
...
client.newCall(request).enqueue(new Callback() {
Handler mainHandler = new Handler(context.getMainLooper());
@Override
public void onFailure(Request request,final Throwable throwable) {
mainHandler.post(new Runnable() {
@Override
public void run() {
cb.onFailure(null, throwable);
}
});
}
@Override
public void onResponse(final Response response) throws IOException {
mainHandler.post(new Runnable() {
@Override
public void run() {
if (!response.isSuccessful()) {
cb.onFailure(response, null);
return;
}
cb.onSuccess(response);
}
});
}
});
}
De acuerdo con la documentación de Retrofit, los métodos de rellamada se ejecutan en el subproceso de la interfaz de usuario de forma predeterminada hasta que proporcione un ejecutor de retrollamada para Retrofit O cuando use tipos de devolución de método personalizados mediante CallAdapterFactory
Sé que es una vieja pregunta, pero recientemente me encontré con el mismo problema. Si necesita actualizar cualquier vista, necesitará usar runOnUiThread()
o publicar el resultado en el hilo principal.
HttpUtil.get(url, new Callback() { //okhttp3.Callback
@Override
public void onFailure(Call call, IOException e) { /* Handle error **/ }
@Override
public void onResponse(Call call, Response response) throws IOException {
String myResponse = response.body().string();
//Do something with response
//...
MyActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
//Handle UI here
findViewById(R.id.loading).setVisibility(View.GONE);
}
});
}
});