您的位置:首页 > 其它

在子线程中调用Toast报Can't create handler inside

2016-06-20 10:32 337 查看
今天在写注册页面时,开辟了一个子线程,接受code返回需要toast提示一下,报错:java.lang.RuntimeException: Can't create handler inside thread that has not calledLooper.prepare()

解决办法: 





      【1】http://czhjchina.blog.163.com/blog/static/20027904720123163149186/




       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 显示需要出现在一个线程的消息队列中.... 很隐蔽

【2】http://tech.cncms.com/shouji/android/96016.html

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;

    }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程