本篇解決兩個問題:
- ViewTree中的繼承關系如何建立的?
- mAttachInfo是如何分發(fā)的?
1:ViewTree的繼承關系如何構建的甘萧?
在ActivityThread中觸發(fā)了handleResumeActivity():
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason){
...
ViewManager wm = a.getWindowManager();
...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
...
}
這里的ViewManager是一個接口间雀,我們知道WindowManager繼承了該接口,同時WindowManager的實現(xiàn)類WindowManagerImpl則將具體實現(xiàn)代理給了WindowManagerGlobal來實現(xiàn)。因此會調用到WindowManagerGlobal的addView():
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
ViewRootImpl是WindowManager和View之間的橋梁辕羽。而ViewRootImpl實現(xiàn)了ViewParent接口。ViewGroup也實現(xiàn)了ViewParent接口垄惧。
ViewRootImpl在setView()中調用了View的assignParent()刁愿,將ViewRootImpl傳入了DecorView中,ViewRootImpl也就成了DecorView的父親到逊。而普通view的這個方法铣口,則是在ViewGroup的addView()中調用,這樣每個View的parent就都賦值了觉壶。
void assignParent(ViewParent parent) {
if (mParent == null) {
mParent = parent;
} else if (parent == null) {
mParent = null;
} else {
throw new RuntimeException("view " + this + " being added, but"
+ " it already has a parent");
}
}
因此我們在Activity的onCreate()中調用:
final View viewById = findViewById(R.id.tv);
viewById.post(new Runnable() {
@Override
public void run() {
ViewParent parent = viewById.getParent();
while (parent!=null){
Log.e("parentName:",parent.getClass().getName());
parent = parent.getParent();
}
}
});
打印的結果:
E/parentName:: androidx.constraintlayout.widget.ConstraintLayout
E/parentName:: androidx.appcompat.widget.ContentFrameLayout
E/parentName:: androidx.appcompat.widget.FitWindowsLinearLayout
E/parentName:: android.widget.FrameLayout
E/parentName:: android.widget.LinearLayout
E/parentName:: com.android.internal.policy.DecorView
E/parentName:: android.view.ViewRootImpl
2:mAttachInfo是如何分發(fā)的脑题?
在ViewRootImpl的構造方法中就將自己傳入到了View的內部類AttachInfo中。
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
那AttachInfo是什么呢铜靶?注釋中這么說的
/**
* {@hide}
*
* Not available for general use. If you need help, hang up and then dial one of the following
* public APIs:
*
* @see #isAttachedToWindow() for current attach state
* @see #onAttachedToWindow() for subclasses performing work when becoming attached
* @see #onDetachedFromWindow() for subclasses performing work when becoming detached
* @see OnAttachStateChangeListener for other code performing work on attach/detach
* @see #getHandler() for posting messages to this view's UI thread/looper
* @see #getParent() for interacting with the parent chain
* @see #getWindowToken() for the current window token
* @see #getRootView() for the view at the root of the attached hierarchy
* @see #getDisplay() for the Display this view is presented on
* @see #getRootWindowInsets() for the current insets applied to the whole attached window
* @see #hasWindowFocus() for whether the attached window is currently focused
* @see #getWindowVisibility() for checking the visibility of the attached window
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
AttachInfo mAttachInfo;
我們可以看到很多重要的屬性叔遂,都保存在該類中。那么mAttachInfo是何時賦值的呢?在View中的該方法中進行賦值:
void dispatchAttachedToWindow(AttachInfo info, int visibility){
mAttachInfo = info;
}
該方法由父ViewGroup來調用
void dispatchAttachedToWindow(AttachInfo info, int visibility){
mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
super.dispatchAttachedToWindow(info, visibility);
mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
combineVisibility(visibility, child.getVisibility()));
}
final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
for (int i = 0; i < transientCount; ++i) {
View view = mTransientViews.get(i);
view.dispatchAttachedToWindow(info,
combineVisibility(visibility, view.getVisibility()));
}
}
首先將當前ViewGroup的繼承父類View中的mAttachInfo進行了賦值已艰,然后對于每一個ChildView中的mAttachInfo進行了賦值痊末。
在ViewRootImpl.setView()中,會調用requestLayout()哩掺。
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true凿叠;
scheduleTraversals();
}
}
scheduleTraversals()開始分發(fā)遍歷任務,
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
將任務交給了Choreographer來處理嚼吞。該類從GPU渲染出來的數(shù)據(jù)的buffer中取出盒件,交給屏幕展示。
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
private void performTraversals() {
...
// 這個host就是DecorView誊薄。而所有的mAttachInfo的分發(fā)也就是從這里開始的履恩。
host.dispatchAttachedToWindow(mAttachInfo, 0);
...
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, mWidth, mHeight);
...
performDraw();
...
}
我們可以看到注釋的這句
host.dispatchAttachedToWindow(mAttachInfo, 0);
將attachInfo分發(fā)給了DecorView,由此觸發(fā)了所有子ViewGroup的分發(fā)呢蔫。其實這些View拿到的mAttachInfo是同一個切心。