首页 > 编程语言 >【报错提示】java.lang.RuntimeException: Can't create handler inside thread

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

时间:2024-08-05 20:54:32浏览次数:10  
标签:lang java void callback Handler 线程 Looper new 报错

报错提示

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

 

标签:lang,java,void,callback,Handler,线程,Looper,new,报错
From: https://www.cnblogs.com/yddbkdz/p/18344036

相关文章

  • JAVA变量类型
    一个类可以包含以下类型变量:局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法......
  • 【Golang 面试 - 进阶题】每日 3 题(十九)
    ✍个人博客:Pandaconda-CSDN博客......
  • [20240804]关于kitty设置与linux LANG环境设置问题.txt
    [20240804]关于kitty设置与linuxLANG环境设置问题.txt--//更正我以前理解的一个混沌的地方:--//我以前个人的工作习惯:LANG=en_US,kittyRemotecharacterset选择Usefontencoding.--//目前这样的设置存在一些问题:--//kitty设置LANG=en_US.UTF-8的情况下,kittywindow->Trans......
  • Maven项目报错:failed to execute goal org.apache.maven.plugins:maven-compiler-plug
    创建了一个maven项目,然后在编译时运行错误:“failedtoexecutegoalorg.apache.maven.plugins:maven-compiler-plugin:3.13.0:compile(default-compile)onprojectforum:thepluginorg.apache.maven.plugins:maven-compiler-plugin:3.13.0requiresmavenversion3.6.3-......
  • java8-常用类型(包装类,BigDecimal,Date等)
    1.包装类1.1包装类简介java语言是面向对象的语言,但是其中的八大基本数据类型不符合面向对象的特征。因此java为了弥补这样的缺点,为这八种基本数据类型专门设计了八种符合面向对象特征的的类型,这八种具有面向对象特征的类型,统称为包装类,英文单词:wrapperclass。包装类,就是......
  • java9-泛型
    1.泛型的简介1.1什么是泛型        泛型是一种特殊的数据类型。它是Java的一个高级特性。在Mybatis、Hibernate这种持久化框架,泛型更是无处不在。在这之前,不管我们在定义成员变量时,还是方法的形参时,都要规定他们的具体类型。所以提出猜想,有没有一种可能,一次声......
  • JSON parse error: Cannot deserialize instance of `java.lang.Long` out of START_O
    这个问题的实际原因就是:    后端id(Long类型)用的雪花算法生成主键id    后端生成id位:1820397662671867904    前端查询id的结果为:1820397662671868000产生的原因:    后端生成为19位,前端接受并展示,使用的类型是number类型是16位   ......
  • java计算字符数量
      publicclassCharCountExample{publicstaticvoidmain(String[]args){Stringtext="Hello,world!Howareyou?";chartargetChar='o';intcount=countChar(text,targetChar);System.out.p......
  • Dzzoffice结合OnlyOffice 报错排查流程总结
    检测OnlyOffice服务是否安装成功首先访问OnlyOffice首页,如下图:出现上图仍旧不能说明你的OnlyOffice服务已经成功安装,我们需要启动OnlyOffice服务测试用例来检测,可以看到上图出现了两条命令,第一条命令是用来启动测试用例服务的,我们只需要在后台执行该命令即可,Win......
  • 给vscode配置clangd插件
    一般情况下,我们在vscode里编辑C/C++代码时用的都是微软的c++package,但是这个插件包很多时候无法正确分析出语法,导致大量虚假错误报告,为了解决这个问题,我们首先禁用掉C++插件包,然后准备更换clangd。clangd是一个基于Clangd的分析服务器,方便我们分析代码语法。为了使用它,......