Android进阶(十六)子线程调用Toast报Can't create handler inside thread that has not called Looper.prepare() 错误

本文详细解析了在Android中子线程调用Toast时出现的Can't create handler inside thread that has not called Looper.prepare()错误。文章深入探讨了错误原因及解决方案,包括Looper.prepare()和Looper.loop()的作用,以及如何确保Toast在非主线程中正确显示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

原子线程调用Toast报Can't create handler inside thread that has not called Looper.prepare() 错误

今天用子线程调Toast报了一个Can't create handler inside thread that has not calledLooper.prepare()错误。

 

因为toast的实现需要在activity的主线程才能正常工作,所以传统的非主线程不能使toast显示在actvity上,通过Handler可以使自定义线程运行于Ui主线程。

 

前几次碰到这个问题,确实郁闷了很久... log -- java.lang.RuntimeException: Can't create handler inside thread that has not calledLooper.prepare()

 

解决办法很简单:

Looper.prepare();

 

Toast.makeText(getApplicationContext(), "test", Toast.LENGTH_LONG).show();

 

Looper.loop();

 

 

为什么要加这两句,看了源码就了解了

Toast 

    public void show() {

      ...

        service.enqueueToast(pkg, tn, mDuration);   //把这个toast插入到一个队列里面

        ...

    }

 

Looper

public static final void prepare() {

        if (sThreadLocal.get() != null) {

            throw new RuntimeException("Only one Looper may be created per thread");

        }

       sThreadLocal.set(new Looper());  //在当前线程中创建一个Looper

    }

 

private Looper() {

        mQueue = new MessageQueue();  //关键在这,创建Looper都干了什么。 其实是创建了消息队列

        mRun = true;

        mThread = Thread.currentThread();

    }

 

 

一般如果不是在主线程中又开启了新线程的话,一般都会碰到这个问题。

原因是在创建新线程的时候默认情况下不会去创建新的MessageQueue。

 

总结下:Toast 显示的必要条件:

1:Toast 显示需要出现在一个线程的消息队列中.... 很隐蔽

 

Android中HandlerThread类的解释

 

Android应用中的消息循环由Looper和Handler配合完成,Looper类用于封装消息循环,类中有个MessageQueue消息队列;Handler类封装了消息投递和消息处理等功能。

系统默认情况下只有主线程(即UI线程)绑定Looper对象,因此在主线程中可以直接创建Handler的实例,但是在子线程中就不能直接new出Handler的实例了,因为子线程默认并没有Looper对象,此时会抛出RuntimeException异常:

  

浏览下Handler的默认构造函数就一目了然了:

 

·如果需要在子线程中使用Handler类,首先需要创建Looper类实例,这时可以通过Looper.prepare()和Looper.loop()函数来实现的。阅读Framework层源码发现,Android为我们提供了一个HandlerThread类,该类继承Thread类,并使用上面两个函数创建Looper对象,而且使用wait/notifyAll解决了多线程中子线程1获取子线程2的Looper对象为空的问题。

Toast创建时需要创建一个Handler,但是这个Handler需要获得Looper的实例,而在子线程中是没有这个实例的,需要手动创建。

 

附Toast部分源码:

 

    public Toast(Context context) {

        mContext = context;

        mTN = new TN();

        mTN.mY = context.getResources().getDimensionPixelSize(

                com.android.internal.R.dimen.toast_y_offset);

    }

 

    private static class TN extends ITransientNotification.Stub {

        ……

        final Handler mHandler = new Handler();    

        ……

    }

 

Handler源码:

 

    /**

     * Default constructor associates this handler with the queue for the

     * current thread.

     *

     * If there isn't one, this handler won't be able to receive messages.

     */

    public Handler() {

        if (FIND_POTENTIAL_LEAKS) {

            final Class<? extends Handler> klass = getClass();

            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&

                    (klass.getModifiers() & Modifier.STATIC) == 0) {

                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +

                    klass.getCanonicalName());

            }

        }

 

        mLooper = Looper.myLooper();

        if (mLooper == null) {

            throw new RuntimeException(

                "Can't create handler inside thread that has not called Looper.prepare()");

        }

        mQueue = mLooper.mQueue;

        mCallback = null;

    }

 

           

给我老师的人工智能教程打call!http://blog.youkuaiyun.com/jiangjunshow
这里写图片描述
<think>好的,我现在需要处理用户关于在安卓10中使用Java编写Thread子线程的示例代码的请求。首先,我得回忆一下安卓开发中的多线程机制。安卓的主线程负责UI操作,子线程用于执行耗时任务,避免阻塞主线程。用户可能需要一个简单的例子来展示如何创建和启动子线程。 接下来,我需要确保代码符合安卓10的环境。虽然Thread本身是Java标准库的一部分,但在安卓中使用时需要注意UI更新的问题。根据引用[2],在子线程中直接更新UI会抛出异常,但Toast的显示可能通过WindowManager处理,有时可能不会触发异常。不过用户可能希望知道如何在子线程执行任务后正确更新UI。 用户提供的引用中有几个相关的点。引用[1]提到了UncaughtExceptionHandler,这可能对处理子线程中的异常有帮助,但用户主要求的是示例代码,这部分可能作为附加内容。引用[4]提到HandlerThreadLooper使用,这可能涉及到更复杂的线程间通信,比如使用Handler将数据从子线程发送到主线程。 因此,示例代码应包括创建Thread子类或实现Runnable接口,启动线程,并在任务完成后通过Handler或runOnUiThread方法更新UI。同时,需要提醒用户不能在子线程中直接操作UI组件,如TextView的setText方法,必须切换到主线程。 另外,考虑到安卓10的特性,可能需要检查是否有API变更,但Thread的基本用法在安卓中相对稳定。所以示例代码可以保持传统写法,但加入必要的上下文,如在Activity中正确使用Handler或runOnUiThread。 最后,用户可能需要相关问题,例如如何传递数据、处理异常或优化线程管理。根据引用[3],RxJava的线程切换可能是一个高级话题,可以作为相关问题之一。</think>### 安卓10中使用Java编写Thread子线程的示例与实现方法 在Android开发中,主线程(UI线程)负责处理用户交互和界面更新,而耗时任务需通过子线程实现以避免阻塞主线程[^2][^4]。以下是基于Android 10的Java子线程实现示例: #### 1. 基础子线程实现(继承Thread类) ```java public class MyThread extends Thread { @Override public void run() { // 子线程执行耗时操作 String result = doBackgroundTask(); // 通过Handler切换到主线程更新UI new Handler(Looper.getMainLooper()).post(() -> { TextView textView = findViewById(R.id.text_view); textView.setText(result); }); } private String doBackgroundTask() { // 模拟耗时操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return "任务完成"; } } // 在Activity中启动线程 new MyThread().start(); ``` #### 2. 实现Runnable接口(推荐方式) ```java Runnable task = new Runnable() { @Override public void run() { // 子线程执行网络请求 String data = fetchDataFromServer(); // 使用runOnUiThread更新UI runOnUiThread(() -> { ProgressBar progressBar = findViewById(R.id.progress_bar); progressBar.setVisibility(View.GONE); Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show(); }); } }; // 启动线程 new Thread(task).start(); ``` #### 关键注意事项: 1. **UI更新限制**:子线程中不能直接操作UI组件(如`TextView.setText()`),必须通过`Handler`或`runOnUiThread`切换到主线程[^2][^4] 2. **线程通信**:推荐使用`Handler`+`Looper`机制或`AsyncTask`(已废弃)进行线程间通信 3. **异常处理**:建议通过`Thread.setUncaughtExceptionHandler`捕获未处理异常[^1] 4. **资源释放**:在Activity销毁时及时终止后台线程防止内存泄漏 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值