緣起
在Android開發中,我們經常會見到下面的代碼,比如:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); System.out.println("onCreate==="); setContentView(R.layout.activity_main); rootBtn = findViewById(R.id.rootBtn); // 代碼1 UIHandler.post(new Runnable() { @Override public void run() { System.out.println("Handler.post==="); } }); // 代碼2 rootBtn.post(new Runnable() { @Override public void run() { System.out.println("View.post==="); } }); }
你曾經有沒有想過這兩者到底有什么區別?我該使用哪種呢?
常見的Handler.post揭秘
Handler的工作機制,網上介紹的文章太多了,這里我就不贅述了,想繼續了解的同學可以參考下這篇文章:Handler源碼分析。一句話總結就是通過Handler對象,不論是post Msg還是Runnable,最終都是構造了一個Msg對象,插入到與之對應的Looper的MessageQueue中,不同的是Running時msg對象的callback字段設成了Runnable的值。稍后這條msg會從隊列中取出來並且得到執行,UI線程就是這么一個基於事件的循環。所以可以看出Handler.post相關的代碼在onCreate里那一刻時就已經開始了執行(加入到了隊列,下次循環到來時就會被真正執行了)。
View.post揭秘
要解釋它的行為,我們就必須深入代碼中去找答案了,其代碼如下:
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { // 注意這個判斷,這個變量時機太早的話是沒值的, // 比如在act#onCreate階段 return attachInfo.mHandler.post(action); } // 仔細閱讀下面這段注釋!!! // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); return true; }
從上面的源碼,我們大概可以看出mAttachInfo字段在這里比較關鍵,當其有值時,其實和普通的Handler.post就沒區別了,但有時它是沒值的,比如我們上面示例代碼里的onCreate階段,那么這時執行到了getRunQueue().post(action);這行代碼,從這段注釋也大概可以看出來真正的執行會被延遲(這里的Postpone注釋);我們接着往下看看getRunQueue相關的代碼,如下:
/** 其實這段注釋已經說的很清楚明了了!!! * Queue of pending runnables. Used to postpone calls to post() until this * view is attached and has a handler. */ private HandlerActionQueue mRunQueue; private HandlerActionQueue getRunQueue() { if (mRunQueue == null) { mRunQueue = new HandlerActionQueue(); } return mRunQueue; }
從上面我們可以看出,mRunQueue就是View用來處理它還沒attach到window(還沒對應的handler)時,客戶代碼發起的post調用的,起了一個臨時緩存的作用。不然總不能丟棄吧,這樣開發體驗就太差了!!!
緊接着,我們繼續看下HandlerActionQueue類型的定義,代碼如下:
public class HandlerActionQueue { private HandlerAction[] mActions; private int mCount; public void post(Runnable action) { postDelayed(action, 0); } public void postDelayed(Runnable action, long delayMillis) { final HandlerAction handlerAction = new HandlerAction(action, delayMillis); synchronized (this) { if (mActions == null) { mActions = new HandlerAction[4]; } mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction); mCount++; } } public void executeActions(Handler handler) { synchronized (this) { final HandlerAction[] actions = mActions; for (int i = 0, count = mCount; i < count; i++) { final HandlerAction handlerAction = actions[i]; handler.postDelayed(handlerAction.action, handlerAction.delay); } mActions = null; mCount = 0; } } private static class HandlerAction { final Runnable action; final long delay; public HandlerAction(Runnable action, long delay) { this.action = action; this.delay = delay; } public boolean matches(Runnable otherAction) { return otherAction == null && action == null || action != null && action.equals(otherAction); } } }
注意:這里的源碼部分,我們只摘錄了部分關鍵代碼,其余不太相關的直接略去了。
從這里可以看出,我們前面的View.post調用里的Runnable最終會被存儲在這里的mActions數組里,這里最關鍵的一點就是其executeActions方法,因為這個方法里我們之前post的Runnable才真正通過handler.postDelayed方式使其進入handler對應的消息隊列里等待執行;
到此為止,我們還差知道View里的mAttachInfo字段何時被賦值以及這里的executeActions方法是什么時候被觸發的,答案就是在View的dispatchAttachedToWindow方法,其關鍵源碼如下:
void dispatchAttachedToWindow(AttachInfo info, int visibility) { mAttachInfo = info; ... // Transfer all pending runnables. if (mRunQueue != null) { mRunQueue.executeActions(info.mHandler); mRunQueue = null; } performCollectViewAttributes(mAttachInfo, visibility); onAttachedToWindow(); ... }
而通過之前的文章,我們已經知道了此方法是當Act Resume之后,在ViewRootImpl.performTraversals()中觸發的,參考View.onAttachedToWindow調用時機分析。
總結
-
Handler.post,它的執行時間基本是等同於onCreate里那行代碼觸達的時間;
-
View.post,則不同,它說白了執行時間一定是在
Act#onResume發生后才開始算的;或者換句話說它的效果相當於你上面的View.post方法是寫在Act#onResume里面的(但只執行一次,因為onCreate不像onResume會被多次觸發); -
當然,雖然這里說的是
post方法,但對應的postDelayed方法區別也是類似的。
平時當你項目很小,MainActivity的邏輯也很簡單時是看不出啥區別的,但當act的onCreate到onResume之間耗時比較久時(比如2s以上),就能明顯感受到這2者的區別了,而且本身它們的實際含義也是很不同的,前者的Runnable真正執行時,可能act的整個view層次都還沒完整的measure、layout完成,但后者的Runnable執行時,則一定能保證act的view層次結構已經measure、layout並且至少繪制完成了一次。
作者:tmp_zhao
鏈接:https://www.jianshu.com/p/7280b2d3b4d1
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
