mAttachInfo来自哪里

1,314 阅读2分钟

上一篇讲解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的知识点后面文章再梳理。