64

我创建了一个助手类来处理我的应用程序中的所有 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了包装 OkhttpCallback类的接口,因为我想改变它的行为,onResponse因此onFailure我可以统一处理由于 i/o 异常导致的失败响应和由于服务器问题导致的失败响应的逻辑。

谢谢。

4

5 回答 5

79

据我了解,Okhttp 回调在主线程上运行,为什么会出现此错误?

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

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

于 2014-06-16T17:22:33.440 回答
46

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

            }
        });
 }
于 2014-06-16T21:12:15.433 回答
20

我知道这是一个老问题,但最近我遇到了同样的问题。如果您需要更新任何视图,您将需要使用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);                
            }
        });
   }
});
于 2016-01-19T15:19:25.753 回答
1

根据 Retrofit 文档,回调方法默认在 UI 线程上执行,直到您向Retrofit提供回调执行程序或使用CallAdapterFactory使用自定义方法返回类型时

于 2018-06-25T18:06:06.567 回答
1

Michael 的上述响应是在调用 Activity 中发生回调的一个很好的解决方案,我将它用作我将在下面概述的解决方案的指南(Kotlin 中的解决方案);

HttpCallback 接口

interface HttpCallback {
    fun onResponse(response: Response)
    fun onFailure(call: Call, e: IOException)
}

接口的实现。我将调用活动作为弱引用传递以执行 UI 更新。

class ImageUploadCallback(activity: WeakReference<Activity>) : HttpCallback {
    val _activity = activity

    override fun onResponse(response: Response) {
        if(_activity.get() != null){
            val activty =  _activity.get()!!

            activty.runOnUiThread(Runnable {
                //Update the UI to your hearts content
            });
        }

    }

    override fun onFailure(call: Call, e: IOException) {
         //Your implemtnation here
    }

}

从活动中调用 HttpProxy 类,将回调作为参数传递

HttpProxy.UploadImage(
                imageToUpload.name,
                imageToUpload,
                MEDIA_TYPE!!,
                //our callback
                ImageUploadCallback(WeakReference(this))

最后,接收回调的 HttpProxy 类中的代码;

fun UploadImage(filename: String, sourceImageFile: File, mediaType: MediaType, callback: HttpCallback) {
   
    //building up our request...and then calling
    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            callback.onFailure(call, e)
        }

        override fun onResponse(call: Call, response: Response) {
            response.use {
                try{
                    callback.onResponse(response)
                }
                catch(e: Exception){

                }
            }
        }
    });
 }
于 2020-11-16T10:28:52.733 回答