我可以從後臺線程調用Snackbar.make()
沒有任何問題。這讓我很驚訝,因爲我認爲UI操作只能從UI線程中獲得。但是這絕對不是這種情況。如何從非UI線程調用Snackbar.make()?
究竟是什麼使Snackbar.make()
與衆不同?爲什麼當你從後臺線程修改它時,這不會導致像任何其他UI組件那樣的異常?
我可以從後臺線程調用Snackbar.make()
沒有任何問題。這讓我很驚訝,因爲我認爲UI操作只能從UI線程中獲得。但是這絕對不是這種情況。如何從非UI線程調用Snackbar.make()?
究竟是什麼使Snackbar.make()
與衆不同?爲什麼當你從後臺線程修改它時,這不會導致像任何其他UI組件那樣的異常?
首先:make()
不執行任何與UI相關的操作,它只是創建一個新的Snackbar
實例。這是對show()
的調用,它實際上將Snackbar
添加到視圖層次結構中,並執行其他危險的與UI相關的任務。但是,您可以從任何線程安全地執行此操作,因爲它可以在UI線程上安排任何顯示或隱藏操作,而不管哪個線程調用show()
。
如需更詳細的解答,讓我們來仔細看看行爲在Snackbar
的源代碼:
讓我們開始在這一切的開始,用您的來電show()
:
public void show() {
SnackbarManager.getInstance().show(mDuration, mManagerCallback);
}
正如你所看到的對show()
的呼叫得到SnackbarManager
的實例,然後將持續時間和回調傳遞給它。 SnackbarManager
是一個單身人士。它負責顯示,安排和管理Snackbar
。現在,讓我們繼續與show()
對SnackbarManager
實現:
public void show(int duration, Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
// Means that the callback is already in the queue. We'll just update the duration
mCurrentSnackbar.duration = duration;
// If this is the Snackbar currently being shown, call re-schedule it's
// timeout
mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
scheduleTimeoutLocked(mCurrentSnackbar);
return;
} else if (isNextSnackbarLocked(callback)) {
// We'll just update the duration
mNextSnackbar.duration = duration;
} else {
// Else, we need to create a new record and queue it
mNextSnackbar = new SnackbarRecord(duration, callback);
}
if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar,
Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
// If we currently have a Snackbar, try and cancel it and wait in line
return;
} else {
// Clear out the current snackbar
mCurrentSnackbar = null;
// Otherwise, just show it now
showNextSnackbarLocked();
}
}
}
現在這個方法的調用是一個稍微複雜一點。我不打算詳細解釋這裏發生了什麼事情,但總體來說,塊可以確保線程安全地撥打show()
。
裏面的塊經理採取解僱當前顯示Snackbars
更新持續時間,或者如果你show()
同一個兩次,當然創造新Snackbars
重新安排照顧。對於每個Snackbar
一個SnackbarRecord
創建其中包含兩個參數最初傳遞給SnackbarManager
,持續時間和回調:
mNextSnackbar = new SnackbarRecord(duration, callback);
在上述方法中調用這種情況發生在中間,在第一如果else語句。
但是,唯一真正重要的部分 - 至少對於這個答案 - 是在底部,showNextSnackbarLocked()
的呼叫。這就是魔術發生的地方,下一個Snackbar正在排隊 - 至少有點類似。
這是showNextSnackbarLocked()
源代碼:
private void showNextSnackbarLocked() {
if (mNextSnackbar != null) {
mCurrentSnackbar = mNextSnackbar;
mNextSnackbar = null;
final Callback callback = mCurrentSnackbar.callback.get();
if (callback != null) {
callback.show();
} else {
// The callback doesn't exist any more, clear out the Snackbar
mCurrentSnackbar = null;
}
}
}
正如你可以看到我們首先檢查是否有小吃吧是通過檢查mNextSnackbar
不爲空排隊。如果不是,我們將SnackbarRecord
設置爲當前的Snackbar
,並從記錄中檢索回調。現在發生了一些類似的事情,經過一個微不足道的空檢查,看看回調是否有效,我們在回調中調用show()
,該回調在Snackbar
類中實現 - 而不是在SnackbarManager
中 - 實際在屏幕上顯示Snackbar
。
起初這可能看起來很奇怪,但它很有意義。 SnackbarManager
只是負責跟蹤Snackbars
的狀態並協調它們,它並不關心Snackbar
的外觀,它是如何顯示的或它甚至是什麼,它只是在正確的時刻將右邊的回調調用show()
方法告訴Snackbar
顯示自己。
讓我們回憶一下,直到現在我們從未離開過後臺線程。 SnackbarManager
的show()
方法中的塊確保沒有其他Thread
會干擾我們所做的所有事情,但主要Thread
上的顯示和解除事件的安排仍然缺失。這不過是要在回調中Snackbar
類的實現,現在當我們看改變:
private final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
@Override
public void show() {
sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, Snackbar.this));
}
@Override
public void dismiss(int event) {
sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0, Snackbar.this));
}
};
所以在回調的消息被髮送到一個靜態的處理器,無論是MSG_SHOW
展現Snackbar
或MSG_DISMISS
再次隱藏它。 Snackbar
本身作爲有效負載附加到消息上。現在,我們幾乎只要我們看看靜態處理的聲明做:因爲它使用的是UI尺蠖(由Looper.getMainLooper()
所示)創建的UI線程上
private static final Handler sHandler;
private static final int MSG_SHOW = 0;
private static final int MSG_DISMISS = 1;
static {
sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MSG_SHOW:
((Snackbar) message.obj).showView();
return true;
case MSG_DISMISS:
((Snackbar) message.obj).hideView(message.arg1);
return true;
}
return false;
}
});
}
所以這個處理程序運行。消息的有效負載 - Snackbar
- 已被播出,然後根據消息的類型在Snackbar
上調用showView()
或hideView()
。 這兩種方法現在都在UI線程上執行!
這兩種方法的實現都很複雜,所以我不會詳細討論每個方案中發生的事情。然而,很明顯,這些方法可以將View
添加到視圖層次結構中,在它出現和消失時對其進行動畫處理,處理CoordinatorLayout.Behaviours
以及其他有關UI的內容。
如果您有任何其他問題隨時問。
滾動,我的答案,我意識到這竟然方式超過它應該是,但是當我看到這樣的源代碼,我不能幫助自己!我希望你能夠深深地感謝你,或者我可能浪費了幾分鐘的時間!
只有創建視圖層次結構的原始線程才能觸及其視圖。
如果使用onPostExecute你就可以訪問視圖
protected void onPostExecute(Object object) { .. }
您完全忽略了這個問題的關鍵。 –
Snackbar.make
是被稱爲形式的非UI線程完全安全的。它在其管理器內部使用了一個處理器,該處理器在主循環線程上運行,從而隱藏了調用者形式的底層複雜性。
管理員中的「處理程序」不起作用。它只是用於通知超時的SnackbarRecords。在Snackbar類中有一個單獨的'Handler',它實際上負責顯示或隱藏'Snackbar'。 –
這仍然不等於2票。 Snackbar是從任何線程調用的完全安全的形式。重點在於它使用主循環中的處理程序來完成其工作。這就是原始問題所有者需要理解的內容。我不能解僱這些倒票,所以我也不會去打他們。 Th – Nazgul
沒有硬的感覺,我沒有downvote:/ –
不,你沒有浪費你的時間,非常感謝:) – q126y