为什么在onResume()方法中可以通过view.post()获取view的宽高,此时view不是应该还尚未绘制完成吗?
我们先来看一下源码
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
源码很简单,只有几行,分mAttachInfo是否为null两种处理方式。那么mAttachInfo是在哪里初始化的呢?我们在View类中搜索一下,发现
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
......
}
我们发现在dispatchAttachedToWindow()方法中,我们为mAttachInfo 赋值,再来追踪一下dispatchAttachedToWindow()方法是在哪里调用的(好吧,其实不是追踪,就是剧透),在ViewRootImpl类中的performTraversals方法里我们发现了踪迹
private void performTraversals() {
final View host = mView;
......
if (mFirst) {
host.dispatchAttachedToWindow(mAttachInfo, 0);
}
......
在view首次被添加的时候,执行了它自己的dispatchAttachedToWindow()方法,并将mAttachInfo作为参数传入。接下来再看一下performTraversals()是什么时候调用的,Ctrl+左键,找到了doTraversal()方法
void doTraversal() {
......
performTraversals();
......
}
继续
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
看一下mTraversalRunnable 是什么时候执行的
void scheduleTraversals() {
......
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
找到scheduleTraversals()方法后,逐渐就清晰了,我们都知道scheduleTraversals()方法在setLayoutParams()、requestLayout()、invalidate()等很多页面重绘时都会调用。
现在我们知道了mAttachInfo是在View首次添加之后初始化赋值的,那在onResume中调用View.post()时,View尚未添加,此时就要走到getRunQueue().post(action)的逻辑中
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
此处返回了一个HandlerActionQueue 对象,来看一下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++;
}
}
......
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);
}
}
}
源码可知,HandlerActionQueue就是一个储存着HandlerAction(封装着Runnable 和delay) 对象的数组,并且在View类的dispatchAttachedToWindow()方法中会调用HandlerActionQueue类内部的executeActions()方法,来执行其缓存的Runnable
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;
}
}
现在,水落石出了,如果view已经添加了,那么mAttachInfo不为null,直接执行attachInfo.mHandler.post(action),如果view尚未添加,mAttachInfo为null,将Runnable缓存进HandlerActionQueue,并在view首次添加,执行了dispatchAttachedToWindow()方法的时候,从缓存中取出Runnable执行。