View.post()方法使用場(chǎng)景
- 子線程中更新ui匾二。
- onCreate()中調(diào)用獲取view寬高哮独。
下面看看源碼
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
這里有兩個(gè)流程:
- AttachInfo不為null時(shí),
attachInfo.mHandler.post(action)->Handler.post()
- AttachInfo為null時(shí)調(diào)用
getRunQueue().post(action)
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
HandlerActionQueue.post()
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++;
}
}
包裝了一層察藐,Runnable和延遲時(shí)間封裝到HandlerAction
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);
}
}
GrowingArrayUtils.append(mActions, mCount, handlerAction)皮璧。保存handlerAction到數(shù)組mActions,mActions是HandlerActionQueue的成員變量分飞。
HandlerActionQueue.executeActions()很明顯操作了HandlerAction悴务。
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;
}
}
最終調(diào)用handler.postDelayed()
當(dāng)然這解釋不了最開始的問題:
- View.post()為何能在子線程中更新ui。
- View.post()為何能在onCreate()中獲取寬高譬猫。
下面看executeActions()調(diào)用處讯檐。
View.dispatchAttachedToWindow()
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
ViewRootImpl.performTraversals()
final View host = mView;
...
host.dispatchAttachedToWindow(mAttachInfo, 0);
...
getRunQueue().executeActions(mAttachInfo.mHandler)
...
//繪制流程
performMeasure(...);
performLayout(...);
performDraw();
performTraversals()是ui繪制入口,這里調(diào)用View.dispatchAttachedToWindow()傳入了AttachInfo删窒。所以AttachInfo不為null意味著view樹已經(jīng)繪制完畢裂垦,前面attachInfo.mHandler.post(action)自然也就能獲取view的寬高。
但是這里有個(gè)問題:dispatchAttachedToWindow()明顯在繪制之前調(diào)用肌索,View.post()如何保證在繪制后執(zhí)行蕉拢?performTraversals()本身也是運(yùn)行在主線程Looper事件循環(huán)中,executeActions()調(diào)用handler.post()發(fā)送消息執(zhí)行傳人的Runnable诚亚,事件隊(duì)列會(huì)先執(zhí)行完performTraversals()才執(zhí)行下一條消息晕换。
mAttachInfo在ViewRootImpl構(gòu)造方法中初始化
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
這里先不說ViewRootImpl,只需知此處mHandler是主線程中初始化的站宗,所以子線程中View.post()可以操作ui闸准。
總結(jié):view樹還未繪制完畢,View.post()傳入的Runnable會(huì)保存在HandlerAction數(shù)組中梢灭。待view樹繪制完畢夷家,ViewRootImpl調(diào)用view.dispatchAttachedToWindow()->HandlerActionQueue.executeActions()->Handler.post()發(fā)送消息,執(zhí)行Runnable敏释;view樹繪制完畢库快,直接執(zhí)行Handler.post()。