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

来源:互联网 发布:最终幻想模型制作软件 编辑:程序博客网 时间:2024/05/22 23:04

一般我们要显示一个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的区别: