Okhttp响应主线程上的回调

Mic*_*ael 61 multithreading android okhttp

我创建了一个帮助程序类来处理我的应用程序中的所有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);
    }

}
Run Code Online (Sandbox Code Playgroud)

然后,在我的主要活动中,我使用这个助手类:

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
            }
        });
Run Code Online (Sandbox Code Playgroud)

onSuccess 代码运行时抛出异常:

android.view.ViewRootImpl $ CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触及其视图.

根据我的理解,Okhttp回调在主线程上运行,为什么我会收到此错误?

**正如一个方面说明,我已经创建HttpCallback界面包Okhttp的Callback类,因为我想改变的行为onResponseonFailure这样我就可以团结的处理逻辑失败,原因是响应I/O异常和失败,原因是服务器问题的响应.

谢谢.

Jak*_*ton 75

根据我的理解,Okhttp回调在主线程上运行,为什么我会收到此错误?

这不是真的.回调在后台线程上运行.如果您想立即在UI中处理某些内容,则需要发布到主线程.

由于您已经有一个回调包装器,您可以在帮助器内部执行此操作,以便HttpCallback在主线程上调用所有方法以方便使用.

  • OkHttp对主线程一无所知.它不是一个Android库,它是一个Java库.此外,通过回调初始数据,它允许流式响应,并可能在整个响应消耗之前逐步更新UI. (18认同)
  • "它不是一个Android库,它是一个Java库" - 现在更有意义.谢谢! (9认同)
  • 是.在回发到主线程之前阅读正文 (5认同)
  • 现在的问题是,在MainActivity中对`response.body()。string()`的调用会抛出`android.os.NetworkOnMainThreadException`(因为它是在主线程上执行的)。此呼叫是否可以进行任何网络操作? (2认同)

Mic*_*ael 42

正如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);
                    }
                });

            }
        });
 }
Run Code Online (Sandbox Code Playgroud)

  • 你可以尝试`new Handler(Looper.getMainLooper())`而不是`new Handler(context.getMainLooper())` (4认同)
  • 你是否在片段或活动的配置更改时遇到了这个帮助器的问题,因为cb会在配置更改之前引用旧对象? (2认同)

Mar*_*nde 17

我知道这是一个老问题,但最近我遇到了同样的问题.如果您需要更新任何视图,则需要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);                
            }
        });
   }
});
Run Code Online (Sandbox Code Playgroud)