アプリですべてのhttp呼び出しを処理するヘルパークラスを作成しました。 okhttpのシンプルなシングルトンラッパーは次のようになります(重要でない部分は省略しました)。
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);
}
}
次に、私の主な活動では、このヘルパークラスを使用します。
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
は、コードの実行時に例外をスローします。
Android.view.ViewRootImpl $ CalledFromWrongThreadException:ビュー階層を作成した元のスレッドのみがそのビューにアクセスできます。
私の理解では、Okhttpコールバックはメインスレッドで実行されますが、なぜこのエラーが発生するのですか?
**補足として、HttpCallback
とCallback
の動作を変更したいので、OkhttpのonResponse
クラスをラップするonFailure
インターフェイスを作成しました。 I/O例外による失敗した応答とサーバーの問題による失敗した応答を処理するロジックを統合できました。
ありがとう。
私の理解では、Okhttpコールバックはメインスレッドで実行されますが、なぜこのエラーが発生するのですか?
本当じゃない。コールバックはバックグラウンドスレッドで実行されます。 UIで何かをすぐに処理する場合は、メインスレッドに投稿する必要があります。
コールバックのラッパーは既にあるので、これをヘルパーで内部的に実行して、すべてのHttpCallback
メソッドがメインスレッドで便利に呼び出されるようにすることができます。
Jake Whartonが示唆したように、メインスレッドでコールバックを明示的に実行する必要がありました。
そこで、コールバックの呼び出しを次のようにRunnable
でラップしました。
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);
}
});
}
});
}
私はそれが古い質問であることを知っていますが、最近同じ問題に遭遇しました。ビューを更新する必要がある場合は、runOnUiThread()
を使用するか、メインスレッドに結果をポストする必要があります。
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);
}
});
}
});
Retrofitのドキュメントによると、 Callback メソッドは、 Retrofit ORを使用してカスタムメソッドを使用する場合、 CallAdapterFactory を使用するタイプ