本文共 2088 字,大约阅读时间需要 6 分钟。
Toast我们平时经常使用,但是你是否了解在子线程中要如何使用Toast呢?
Toast的一般姿势
平时我们经常在主线程中直接使用Toast,代码看起来会像下面这样
Toast.makeText(MainActivity.this, "", Toast.LENGTH_SHORT).show();
但是如果在子线程调用是不会有toast弹出的
Toast的正确姿势
如果在子线程调用那么让Toast能正常显示的方式是在它之前和之后调用Looper.prepare()和Looper.loop()
Looper.prepare();Toast.makeText(MainActivity.this, "", Toast.LENGTH_SHORT).show();Looper.loop();
原因是什么呢
我们得从源码角度来分析,看看在Toast show()的时候做了些什么
public void show() { if (mNextView == null) { throw new RuntimeException("setView must have been called"); } INotificationManager service = getService(); String pkg = mContext.getOpPackageName(); TN tn = mTN; tn.mNextView = mNextView; try { service.enqueueToast(pkg, tn, mDuration); } catch (RemoteException e) { // Empty }}
所以Toast其实是通过NotificationManagerService来实现Toast的展示的,而传给他的参数里的 mTn又是什么呢,
其实它是Toast的一个内部类,它有两个方法,show()和hide()是用来给NotificationManagerService回调的,可以看看它的代码private static class TN extends ITransientNotification.Stub { .... /** * schedule handleShow into the right thread */ @Override public void show(IBinder windowToken) { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.obtainMessage(SHOW, windowToken).sendToTarget(); } /** * schedule handleHide into the right thread */ @Override public void hide() { if (localLOGV) Log.v(TAG, "HIDE: " + this); mHandler.obtainMessage(HIDE).sendToTarget(); }
因此可以看出来,Toast通过 NotificationManagerService来统一调度 Toast,而 NotificationManagerService回调 TN 的show()来往对应的线程发消息,
既然是handler实现,那么来看看它的实现代码,就在TN的构造方法里有这么一段
if (looper == null) { // Use Looper.myLooper() if looper is not specified. looper = Looper.myLooper(); if (looper == null) { throw new RuntimeException( "Can't toast on a thread that has not called Looper.prepare()"); }}mHandler = new Handler(looper, null) {....
因此没有调用prepare()和启动消息队列的话,在子线程调用Toast是显示不出来的。
总结
Toast在主线程的显示只需要调用show()就可以,如果想在子线程调用,则需要在子线程启动Looper,这样才能有消息队列来承载Handler收发消息。否则子线程的Toast是不能显示的
更多Android进阶技术,面试资料系统整理分享,职业生涯规划,产品,思维,行业观察,谈天说地。可以加Android架构师群;701740775。
转载地址:http://tfbza.baihongyu.com/