view.post()
[View.java]
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;
}
[ViewRootImpl.java]
static final class RunQueue {
private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
void post(Runnable action) {
postDelayed(action, 0);
}
void postDelayed(Runnable action, long delayMillis) {
HandlerAction handlerAction = new HandlerAction();
handlerAction.action = action;
handlerAction.delay = delayMillis;
synchronized (mActions) {
mActions.add(handlerAction);
}
}
如果 mAttachInfo != null 则 attachInfo.mHandler.post(action) 这个 attachInfo.mHandler 是什么,在哪初始化不细讲,下文会提到 mAttachInfo 的初始化,反正不管怎么说一定是主线程的 Looper。这个条件下,view.post() 和 handler.post() 可以说用起来没区别
如果 view 的 mAttachInfo == null 就把 Runnable action 加入到 ViewRootImpl 的队列 mActions 中,那什么时候执行呢?
[ViewRootImpl.java]
private void performTraversals() {
final View host = mView;
...
host.dispatchAttachedToWindow(mAttachInfo, 0); // ViewGroup 会递归,设置 view 的 mAttachInfo
...
getRunQueue().executeActions(mAttachInfo.mHandler); // 遍历队列 handler.post(action),
...
measureHierarchy(...)
...
performLayout(...)
...
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() ||
viewVisibility != View.VISIBLE;
if (!cancelDraw && !newSurface) { // 这边我也不清楚,待继续学习
if (!skipDraw || mReportNextDraw) {
...
performDraw();
}
} else {
if (viewVisibility == View.VISIBLE) {
// Try again
scheduleTraversals();
} else if
...
}
...
}
从图中可以看到
a) new ViewRootImpl() 是在 Activity onResume() 之后
题外话,这点比较有意思,更新 UI 判断是否主线程其实都是在 ViewRootImpl 中
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
那没有初始化 ViewRootImpl 之前,在子线程更新 UI 会怎样?答案是没有问题。可以在 Activity 的 onCreate onResume 中写个子线程更新 UI 试试,当然你不能太耗时。
b) ViewRootImpl performTraversals() 是经过 handler 排队的
c) performTraversals() 中 getRunQueue().executeActions(mAttachInfo.mHandler) 又是放入 handler 排队
所以 view.post(runnable) runnable 执行的时机肯定是在 view 的 measure、layout 之后,经过调试得知最终结果是
onCreate→
onResume→
new ViewRootImpl requestLayout→
====经过 handler→performTraversals→
========view measure→view layout→
========经过 handler→view posted runnable→
========经过 handler→onWindowFocusChanged→
========经过 handler→performTraversals→
============view measure→view layout→view draw
所以想要测试某个 view 的绘制时间,还是在 onDraw 中计算比较准确(2层 post 也差不多)
runOnUiThread()
[Activity.java]
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
与 handler.post() 的区别就是如果当前线程是主线程则直接执行,不用排队了