Okhttp响应主线程上的回调

时间:2014-06-16 15:07:49

标签: android multithreading 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);
    }

}

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

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接口来包装Okhttp的Callback类,因为我想改变onResponseonFailure的行为所以我可以统一处理因i / o异常而导致失败响应的逻辑以及由于服务器问题导致的响应失败。

感谢。

5 个答案:

答案 0 :(得分:69)

  

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

事实并非如此。回调在后台线程上运行。如果您想立即在UI中处理某些内容,则需要发布到主线程。

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

答案 1 :(得分:40)

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

            }
        });
 }

答案 2 :(得分:15)

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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){

                }
            }
        }
    });
 }