我创建了一个帮助程序类来处理我的应用程序中的所有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
类,因为我想改变onResponse
和onFailure
的行为所以我可以统一处理因i / o异常而导致失败响应的逻辑以及由于服务器问题导致的响应失败。
感谢。
答案 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){
}
}
}
});
}