上一篇讲解View.post(Runnable action)方法的时候知道,action何时执行取决于mAttachInfo是否为空,那么①mAttachInfo什么时候会被赋值,②又是在来自哪里呢,下面咱们进入到源码中寻找答案。
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;
}
由上图代码可知mAttachInfo是View的成员变量,然后搜索mAttachInfo被赋值的地方,发现只有一处赋值
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
...
}
接下来寻找dispatchAttachedToWindow(AttachInfo info, int visibility)被调用的地方,View里面没有调用,于是就去ViewGroup里面去找
private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) {
...
AttachInfo ai = mAttachInfo;
if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
boolean lastKeepOn = ai.mKeepScreenOn;
ai.mKeepScreenOn = false;
child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
if (ai.mKeepScreenOn) {
needGlobalAttributesUpdate(true);
}
ai.mKeepScreenOn = lastKeepOn;
}
...
}
addViewInner就很眼熟了吧,对,就是addView最终调用的方法
public void addView(View child, int index, LayoutParams params) {
if (DBG) {
System.out.println(this + " addView");
}
if (child == null) {
throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
}
// addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
public void addView(View child, int index) {
if (child == null) {
throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
}
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
}
}
addView(child, index, params);
}
public void addView(View child) {
addView(child, -1);
}
那么回到我们最初探索的目的,① mAttachInfo什么时候会被赋值找到了,第②个疑问mAttachInfo来自哪里?
从View和ViewGroup的代码中可以看到mAttachInfo都是从parent传入进来,最根的View是DecorView,而DecorView又是被addView到了ViewRootImpl上(ViewRootImpl不是View,但是它实现了ViewParent,所以也有addView方法),那我们就去ViewParent中去寻找
public ViewRootImpl(Context context, Display display) {
...
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
...
}
第二个答案也揭晓了,mAttachInfo的创建时机在ViewRootImpl的构造方法中,也就是创建ViewRootImpl的时候mAttachInfo就被创建好了,ViewRootImpl的知识点后面文章再梳理。