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

标签: 线程threadjavaandroid
49821人阅读 评论(0) 收藏 举报
分类:

原子线程调用ToastCan'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 显示的必要条件:

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

 

AndroidHandlerThread类的解释

 

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

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

  

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

 

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

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;

    }

 

1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:23378397次
    • 积分:57540
    • 等级:
    • 排名:第50名
    • 原创:470篇
    • 转载:39篇
    • 译文:2篇
    • 评论:256条
    流量统计
    你问我答
     有问题?联系我。

    博客专栏
    汪国真--《热爱生命》
          热 爱 生 命
           汪国真

        我不去想是否能够成功

        既然选择了远方

        便只顾风雨兼程

        我不去想能否赢得爱情

        既然钟情于玫瑰

        就勇敢地吐露真诚

        我不去想身后会不会袭来寒风冷雨

        既然目标是地平线

        留给世界的只能是背影

        我不去想未来是平坦还是泥泞

        只要热爱生命

        一切都在意料之中
    轻松一下