在子线程创建并显示一个Toast

一般我们要显示一个Toast的时候,默认情况下都是在主线程中有如下做法:

Toast.makeText(mContext, "我是一个Toast", Toast.LENGTH_LONG).show();

我们看看makeText里面干了什么:

public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
        Toast result = new Toast(context);

        LayoutInflater inflate = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
        TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
        tv.setText(text);
        
        result.mNextView = v;
        result.mDuration = duration;

        return result;
    }

默认情况下,会从系统xml文件中获得一个Toast布局文件,也就是我们最终显示出来的样式。在这之前会用构造函数创建一个Toast对象实例。

public Toast(Context context) {
        mContext = context;
        mTN = new TN();
        mTN.mY = context.getResources().getDimensionPixelSize(
                com.android.internal.R.dimen.toast_y_offset);
        mTN.mGravity = context.getResources().getInteger(
                com.android.internal.R.integer.config_toastDefaultGravity);
    }

关键看这个内部对象TN,其中有个成员变量是handler,在创建TN对象的时候就初始化了:

final Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                IBinder token = (IBinder) msg.obj;
                handleShow(token);
            }
        };

我们知道初始化一个Handler对象是必须要有looper对象的,如下:

public Handler(Callback callback, boolean async) {
        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 = callback;
        mAsynchronous = async;
    }

它会去得到当前线程的looper对象,如果得不到就会报异常。

一般情况下我们是在主线程创建的Toast,主线程默认在ActivityThread的main方法中就执行了looper的初始化,所以不会报错。

所以我们如果想在子线程中创建一个Toast对象,那么就必须先创建一个Looper对象。如下:

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                    Looper.prepare();
                    Toast.makeText(ThreadTostActivity.this, "我是一个线程里的Toast", Toast.LENGTH_LONG).show();
                    Looper.loop();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

使用Looper.prepare方法在当前线程创建了looper,然后makeText的时候,会创建handler,该handler自然就跟当前线程里的looper对象关联上了。于是就能显示出来了。




相似的,dialog也可以在线程中执行,其内部也是有个成员变量handler,在定义的时候就已经初始化了。

另外使用dialog需要注意的就是必须依附于activity,如果要在service中使用dialog则要另外设置:

http://blog.csdn.net/huxueyan521/article/details/8954844

原因如下:

http://www.jianshu.com/p/413ec659500a
大致就是因为在创建dialog的时候,我们传进来的context的区别,传进来activity的context时parentWindow不会为空,但传进来其他类型的context,则会导致parentWindow为空,一旦为空,接下来在添加到window的时候就报异常了。

另附,activity中的context和application和service的context的区别:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值