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。