【报错提示】java.lang.RuntimeException: Can't create handler inside thread

报错提示

遇到一个报错: 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。

 

posted @ 2024-08-05 20:55  灿烂热烈  阅读(290)  评论(0编辑  收藏  举报