报错提示
遇到一个报错:
java.lang.RuntimeException: Can't create handler inside thread Thread[OkHttp https://a.fxltsbl.com/...] that has not called Looper.prepare()
分析
1. 这个报错提示是在一个没有调用 Looper.prepare() 的线程中尝试创建一个 Handler 对象。
在 Android 开发中这是不允许的。在 Android 中,Handler 通常用于与 UI 线程进行通信,因此必须在 UI 线程上创建和使用它。
2. 根据报错信息:
java.lang.RuntimeException: Can't create handler inside thread Thread[OkHttp https://a.fxltsbl.com/...] that has not called Looper.prepare()
可以看出问题出现在名为 OkHttp https://a.fxltsbl.com/... 的线程中,这是一个非 UI 线程,但尝试在这个线程上创建了 Handler。
解决方法
解决方法通常是确保 Handler 的创建和使用发生在主线程(UI 线程)上,或者如果需要在后台线程中使用 Handler,则需要先创建一个与该线程关联的 Looper。以下两种解决方法参考:
1. 第一种方法
可以从报错看出:当前在子线程中尝试创建了一个 Handler 实例,而 Android 的 Handler 需要在主线程中使用,因为它依赖于主线程的消息循环(Looper)。
如果在代码中,我们使用的网络请求的回调方法是在 OkHttp 的工作线程中执行的,那么直接在这里创建 Handler 是不合适的。
解决这个问题的方法是,在回调方法中确保使用主线程来执行 callback.onFailure() 和 callback.onResponse() 方法。
Android 中有几种方式可以切换到主线程,最常见的是使用 runOnUiThread() 方法或者使用 Handler 来进行线程间通信。
修改后的代码示例,使用 runOnUiThread() 来确保在主线程执行回调:
private static void setHttpRequestAgent() {
//创建okhttpclient对象
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
//创建Request 对象
Request request = new Request.Builder()
.url("https://api.github.com/")
.build();
//创建call对象
Call call = okHttpClient.newCall(request);
//通过call.enqueue方法来提交异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
// 在这里处理异步请求失败的逻辑,切换到主线程执行回调
runOnUiThread(() -> callback.onFailure(e.getMessage()));
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
// 在这里处理异步请求成功的逻辑,切换到主线程执行回调
runOnUiThread(() -> {
try {
callback.onResponse(response.code(), response.body().string());
} catch (IOException e) {
callback.onFailure(e.getMessage());
}
});
}
});
}
});
}
// 在 Activity 或者 Fragment 中定义一个辅助方法 runOnUiThread,用于确保在主线程执行任务
private void runOnUiThread(Runnable action) {
new Handler(Looper.getMainLooper()).post(action);
}
在上面的代码中,使用了 runOnUiThread() 方法来确保在主线程上执行 callback.onFailure() 和 callback.onResponse()。这样可以避免在子线程中直接操作 UI 导致的问题。
2. 第二种方法
如果希望在子线程中使用 Handler
,需要先调用 Looper.prepare()
初始化一个 Looper,并且在使用完毕后调用 Looper.loop()
来启动消息循环。
这样可以确保在子线程中正确使用 Handler
来进行消息处理,包括处理网络请求的回调。
修改后的示例代码,以在子线程中使用 Handler
处理网络请求的回调:
private static void setHttpRequestAgent() {
OkHttpClient client = new OkHttpClient.Builder().build();
MTSDK.getInstance().setHttpRequestAgent(new HttpRequestAgent() {
@Override
public void request(String protocol, String url, String params, HttpRequestCallback callback) {
new Thread(() -> {
// 初始化 Looper
Looper.prepare();
Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_FAILURE:
callback.onFailure((String) msg.obj);
break;
case MSG_RESPONSE:
ResponseData responseData = (ResponseData) msg.obj;
callback.onResponse(responseData.code, responseData.body);
break;
}
}
};
RequestBody body = RequestBody.create(params, MediaType.parse("application/json; charset=utf-8"));
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
// 使用之前创建的 OkHttpClient 实例 client 进行网络请求
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
// 处理请求失败的逻辑,通过 Handler 发送消息到主线程处理回调
Message message = handler.obtainMessage(MSG_FAILURE, e.getMessage());
message.sendToTarget();
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
// 处理请求成功的逻辑,通过 Handler 发送消息到主线程处理回调
String responseBody = response.body().string();
ResponseData responseData = new ResponseData(response.code(), responseBody);
Message message = handler.obtainMessage(MSG_RESPONSE, responseData);
message.sendToTarget();
}
});
// 启动消息循环
Looper.loop();
}).start();
}
});
}
// 定义一个简单的数据类用于存储响应信息
private static class ResponseData {
int code;
String body;
ResponseData(int code, String body) {
this.code = code;
this.body = body;
}
}
// 定义消息类型
private static final int MSG_FAILURE = 1;
private static final int MSG_RESPONSE = 2;
上面的代码中,在 request
方法中创建了一个新的线程,并在该线程中初始化了一个 Looper 和 Handler。
在网络请求的回调方法中,通过 Handler 发送消息到主线程处理回调,确保了在子线程中正确使用了 Looper 和 Handler 来进行线程间通信。
这种方法适合于需要在子线程中处理网络请求的情况,并且需要将结果传递回主线程更新 UI。