1 注意的问题
View.post(Runnable r)很多时候在子线程调用,用于进行子线程无法完成的操作,或者在该方法中通过getMeasuredWidth()获取view的宽高。需要注意的是,在子线程调用该函数,可能不会被执行,原因是该view不是attached状态。
2 View.post源码分析
- 2.1 View
private HandlerActionQueue mRunQueue;
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
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;
}
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
如果view已经attached,则调用ViewRootImpl中的ViewRootHandler,放入主线程Lopper等待执行。如果detach,则将其暂存在RunQueue当中,等待其它线程取出执行。
- 2.2 HandlerActionQueue
private HandlerAction[] mActions;
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;
}
}
暂存在RunQueue中的Runnable,在executeActions时被取出。
-
2.3 ViewRootImpl
在performTraversals()中调用
performTraversals.png
static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
static RunQueue getRunQueue() {
RunQueue rq = sRunQueues.get();
if (rq != null) {
return rq;
}
rq = new RunQueue();
sRunQueues.set(rq);
return rq;
}
//performTraversals内部
void performTraversals(){
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(attachInfo.mHandler);
……
if (!mStopped) {
……
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
……
}
……
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
……
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
……
performDraw();
}
} else {
if (viewVisibility == View.VISIBLE) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
可以看到,如果在detached的状态下调用post方法,在主线程的ViewRootImpl中调用getRunQueue时,由于RunQueue是线程私有,所以会获取不到原来存放runnable的RunQueue,继而新建一个新的RunQueue,当然就没有执行我们想执行的代码了。
3 什么时候attach和detach?
onResume()调用完成之后,回调View的onAttachToWindow()之前attach。
onDestroy()调用完成之后,回调View的onDettachToWindow()之前detach。
即如果在主线程调用,任何时候都可以执行。在子线程调用,必须在该view处于attached状态下才能保证执行。