Toast在子執行緒呼叫的問題
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。