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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sunhuaqiang1/article/details/46839045

原子线程调用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;

    }

 

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试