Android View.post(Runable)某些情況不執行的原因
轉載請注明http://blog.csdn.net/chen_senhua/article/details/52202413
前幾天在改項目UI的時候,發現一個bug,第一次進入APP的時候有個Bitmap不顯示,
點選換圖後又能顯示,打個斷點,才發現問題出在View.post(Runable),竟然沒有執行。
之前也沒看裡面的實作,隻是猜測裡面有個Handler,現在有空就來看看裡面的實作
打開View類找到方法
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}
首先看mAttachInfo 何時指派的
從代碼中可以猜出,十有八九是mAttachInfo ==null 導緻不執行的,既然是初始化的時候不顯示,可以推斷是指派問題,找到mAttachInfo 的指派方法
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
//...
onAttachedToWindow();
//...
}
可以看到指派是在 dispatchAttachedToWindow 的時候進行的,而dispatchAttachedToWindow 是在 ViewRootImpl 類的performTraversals 調用的,這方法在視圖的初始化會調用,在視圖發生某些改變時也會調用。(ViewRootImpl 是View 和WindowManager 的互動類).
是以可以得出結論在onAttachedToWindow之後post,必定是有效果的。
然後再看看mAttachInfo 沒有被指派的情況下怎麼處理的
注釋說“Assume that post will succeed later“我隻是assume 他會執行,意味着-那就有一定幾率不執行咯, 這個情況下他把runable放到一個隊列中了,到底是放哪了呢,再看代碼
static RunQueue getRunQueue() {
RunQueue rq = sRunQueues.get();
if (rq != null) {
return rq;
}
rq = new RunQueue();
sRunQueues.set(rq);
return rq;
}
其中 sRunQueues 是ThreadLocal的執行個體,(ThreadLocal是一個和線程綁定的資料存儲類,在不同的線程set的值得在相應相應線程才能取到,大體實作是把自身作為鍵值放到了Thread的Map中),他把runable放到一個ThreadLocal中去了,那麼他何時去取呢?再次打開ViewRootImpl 類
performTraversals()
{
//..
getRunQueue().executeActions(mAttachInfo.mHandler,0)
//..
}
void executeActions(Handler handler) {
synchronized (mActions) {
final ArrayList<HandlerAction> actions = mActions;
final int count = actions.size();
for (int i = ; i < count; i++) {
final HandlerAction handlerAction = actions.get(i);
handler.postDelayed(handlerAction.action,
handlerAction.delay);
}
actions.clear();
}
}
上面可以看出runable繞了一圈又回到了mAttachInfo.mHandler 的懷抱。
是以我們可以得出如下結論
1)在View的onAttachedToWindow 之後進行post,不論在什麼線程都會調用,postDelay同理。
2)在View的onAttachedToWindow 之前進行post,在主線程,可能要隔一段時間才會調用(等待 下一次 performTraversals ),在支線沒有特殊處理不會被調用,postDelay同理,并且delay的時間偏差可能較大。
3)在一個View沒有添加到視窗中時,也就不會調用onAttachedToWindow ,是以效果同第二條。