public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
…
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
// 调用 ViewGroup.invalidateChild()
p.invalidateChild(this, damage);
}
…
}
这里调用到 ViewGroup.invalidateChild()
。
ViewGroup.java
public final void invalidateChild(View child, final Rect dirty) {
final AttachInfo attachInfo = mAttachInfo;
…
ViewParent parent = this;
if (attachInfo != null) {
…
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
…
parent = parent.invalidateChildInParent(location, dirty);
…
} while (parent != null);
}
}
这里有一个递归,不停的调用父 View 的 invalidateChildInParent()
方法,直到最顶层父 View 为止。这很好理解,仅靠 View 本身是无法绘制自己的,必须依赖最顶层的父 View 才可以测量,布局,绘制整个 View 树。但是最顶层的父 View 是谁呢?是 setContentView()
传入的布局文件吗?不是,它解析之后被塞进了 DecorView
中。是 DecorView 吗
?也不是,它也是有父亲的。
DecorView 的 parent 是谁呢?这就得来到 ActivityThread.handleResume()
方法中。
ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
…
// 1. 回调 onResume()
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
…
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
// 2. 添加 decorView 到 WindowManager
wm.addView(decor, l);
…
}
第二步中实际调用的是 WindowManagerImpl.addView()
方法,WindowManagerImpl
中又调用了 WindowManagerGlobal.addView()
方法。
WindowManagerGlobal.java
// 参数 view 就是 DecorView
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
…
ViewRootImpl root;
// 1. 初始化 ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
mViews.add(view);
mRoots.add(root);
// 2. 重点在这
root.setView(view, wparams, panelParentView);
…
}
跟进 ViewRootImpl.setView()
方法。
ViewRootImpl.java
// 参数 view 就是 DecorView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
// 1. 发起首次绘制
requestLayout();
// 2. Binder 调用 Session.addToDisplay(),将 window 添加到屏幕
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
// 3. 重点在这,注意 view 是 DecorView,this 是 ViewRootImpl 本身
view.assignParent(this);
}
}
}
跟进 View.assignParent()
方法。