关于View中mParent的来龙去脉

作者: jtsky | 来源:发表于2017-10-11 17:48 被阅读1133次

    以下代码来自android-26

    mParent赋值

    View#assignParent

    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");
            }
        }
    

    下面的分析我们会分三部分来分析,第一部分是DecorView的由来,第二部分是DecorView的mParent,而第三部分是普通view(DecorView的子view)的mParent。

    首先我们看下View#assignParent都在哪些地方被调用了,方便我们在源码的海洋中不至于迷失。


    image.png

    DecorView的由来

    首先我们得知道assignParent是在什么地方被调用的。其实是在ViewRootImpl#setView中被调用的。接下来我们一步步分析。我们知道只有startActivity()开启一个新的activity的时候页面才会被渲染。而startActivity会一步步调用到ActivityThread#performLaunchActivity方法。

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
    
            ActivityInfo aInfo = r.activityInfo;
            if (r.packageInfo == null) {
                r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                        Context.CONTEXT_INCLUDE_CODE);
            }
    
            ComponentName component = r.intent.getComponent();
            if (component == null) {
                component = r.intent.resolveActivity(
                    mInitialApplication.getPackageManager());
                r.intent.setComponent(component);
            }
    
            if (r.activityInfo.targetActivity != null) {
                component = new ComponentName(r.activityInfo.packageName,
                        r.activityInfo.targetActivity);
            }
    
            ContextImpl appContext = createBaseContextForActivity(r);
            Activity activity = null;
            try {
                java.lang.ClassLoader cl = appContext.getClassLoader();
                activity = mInstrumentation.newActivity(
                        cl, component.getClassName(), r.intent);
                StrictMode.incrementExpectedActivityCount(activity.getClass());
                r.intent.setExtrasClassLoader(cl);
                r.intent.prepareToEnterProcess();
                if (r.state != null) {
                    r.state.setClassLoader(cl);
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(activity, e)) {
                    throw new RuntimeException(
                        "Unable to instantiate activity " + component
                        + ": " + e.toString(), e);
                }
            }
    
            try {
                Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    
                if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
                if (localLOGV) Slog.v(
                        TAG, r + ": app=" + app
                        + ", appName=" + app.getPackageName()
                        + ", pkg=" + r.packageInfo.getPackageName()
                        + ", comp=" + r.intent.getComponent().toShortString()
                        + ", dir=" + r.packageInfo.getAppDir());
    
                if (activity != null) {
                    CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                    Configuration config = new Configuration(mCompatConfiguration);
                    if (r.overrideConfig != null) {
                        config.updateFrom(r.overrideConfig);
                    }
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                            + r.activityInfo.name + " with config " + config);
                    Window window = null;
                    if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                        window = r.mPendingRemoveWindow;
                        r.mPendingRemoveWindow = null;
                        r.mPendingRemoveWindowManager = null;
                    }
                    appContext.setOuterContext(activity);
                    activity.attach(appContext, this, getInstrumentation(), r.token,
                            r.ident, app, r.intent, r.activityInfo, title, r.parent,
                            r.embeddedID, r.lastNonConfigurationInstances, config,
                            r.referrer, r.voiceInteractor, window, r.configCallback);
    
                    if (customIntent != null) {
                        activity.mIntent = customIntent;
                    }
                    r.lastNonConfigurationInstances = null;
                    checkAndBlockForNetworkAccess();
                    activity.mStartedActivity = false;
                    int theme = r.activityInfo.getThemeResource();
                    if (theme != 0) {
                        activity.setTheme(theme);
                    }
    
                    activity.mCalled = false;
                    //注意下面的代码 这是activity生命周期开始的地方
                    if (r.isPersistable()) {
                        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                    } else {
                        mInstrumentation.callActivityOnCreate(activity, r.state);
                    }
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onCreate()");
                    }
                    r.activity = activity;
                    r.stopped = true;
                    if (!r.activity.mFinished) {
                        activity.performStart();
                        r.stopped = false;
                    }
                    if (!r.activity.mFinished) {
                        if (r.isPersistable()) {
                            if (r.state != null || r.persistentState != null) {
                                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                        r.persistentState);
                            }
                        } else if (r.state != null) {
                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                        }
                    }
                    if (!r.activity.mFinished) {
                        activity.mCalled = false;
                        if (r.isPersistable()) {
                            mInstrumentation.callActivityOnPostCreate(activity, r.state,
                                    r.persistentState);
                        } else {
                            mInstrumentation.callActivityOnPostCreate(activity, r.state);
                        }
                        if (!activity.mCalled) {
                            throw new SuperNotCalledException(
                                "Activity " + r.intent.getComponent().toShortString() +
                                " did not call through to super.onPostCreate()");
                        }
                    }
                }
                r.paused = true;
    
                mActivities.put(r.token, r);
    
            } catch (SuperNotCalledException e) {
                throw e;
    
            } catch (Exception e) {
                if (!mInstrumentation.onException(activity, e)) {
                    throw new RuntimeException(
                        "Unable to start activity " + component
                        + ": " + e.toString(), e);
                }
            }
    
            return activity;
        }
    

    我们看到调用了mInstrumentation.callActivityOnCreate(activity, r.state);;

    Instrumentation#callActivityOnCreate&Activity#performCreate

    public void callActivityOnCreate(Activity activity, Bundle icicle) {
            prePerformCreate(activity);
            activity.performCreate(icicle);
            postPerformCreate(activity);
        }
    
    Activity#performCreate
    
    final void performCreate(Bundle icicle) {
            restoreHasCurrentPermissionRequest(icicle);
            onCreate(icicle);
            mActivityTransitionState.readState(icicle);
            performCreateCommon();
        }
    

    我们一般都会在onCreate()方法中调用setContentView方法

    Activity#setContentView

    public void setContentView(@LayoutRes int layoutResID) {
            getWindow().setContentView(layoutResID);
            initWindowDecorActionBar();
        }
    
    image.png

    而getWindow其实是PhoneWindow,PhoneWindow其实是属于内核代码,不属于app进程,我们进去看看。

    PhoneWindow#setContentView

    @Override
        public void setContentView(int layoutResID) {
            // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
            // decor, when theme attributes and the like are crystalized. Do not check the feature
            // before this happens.
            if (mContentParent == null) {
                //进行了DecorView的初始化
                installDecor();
            } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                mContentParent.removeAllViews();
            }
    
            if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
                final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                        getContext());
                transitionTo(newScene);
            } else {
                mLayoutInflater.inflate(layoutResID, mContentParent);
            }
            mContentParent.requestApplyInsets();
            final Callback cb = getCallback();
            if (cb != null && !isDestroyed()) {
                cb.onContentChanged();
            }
            mContentParentExplicitlySet = true;
        }
    

    到这为止我们已经知道了DecorView是在PhoneWindow#setContentView被赋值的,而PhoneWindow#setContentView又是通过Activity#setContentView一步步调用得到的。

    20160323161350830.png

    DecorView.mParent的真相

    下面我们换个思路,大家如果去看过我的从startActivity一步步到穿越进程壁垒就会知道,当调用startActivity的时候会进行跨进程调用,最终会调用到ApplicationThread#scheduleLaunchActivity,而ApplicationThread是ActivityThread的内部类。

    ApplicationThread#scheduleLaunchActivity

    public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
             CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
             int procState, Bundle state, PersistableBundle persistentState,
             List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
             boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
    
         updateProcessState(procState, false);
    
         ActivityClientRecord r = new ActivityClientRecord();
    
         r.token = token;
         r.ident = ident;
         r.intent = intent;
         r.referrer = referrer;
         r.voiceInteractor = voiceInteractor;
         r.activityInfo = info;
         r.compatInfo = compatInfo;
         r.state = state;
         r.persistentState = persistentState;
    
         r.pendingResults = pendingResults;
         r.pendingIntents = pendingNewIntents;
    
         r.startsNotResumed = notResumed;
         r.isForward = isForward;
    
         r.profilerInfo = profilerInfo;
    
         r.overrideConfig = overrideConfig;
         updatePendingConfiguration(curConfig);
         //注意这行代码
         sendMessage(H.LAUNCH_ACTIVITY, r);
     }
    

    可以看到sendMessage会切换到UI线程继续进行有关UI的操作,而代码会进入到ActivityThread的内部类H extends Handler 。

    ActivityThread#H#handleMessage

    public void handleMessage(Message msg) {
    switch (msg.what) {
                    case LAUNCH_ACTIVITY: {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                        final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
    
                        r.packageInfo = getPackageInfoNoCheck(
                                r.activityInfo.applicationInfo, r.compatInfo);
                        handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    } break;
                ……
            }
    }
    

    ActivityThread#handleLaunchActivity

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
          ……
            // Initialize before creating the activity
            WindowManagerGlobal.initialize();
            //在上文中我们知道performLaunchActivity最终会的初始化DecorView
            Activity a = performLaunchActivity(r, customIntent);
            ……
            handleResumeActivity(r.token, false, r.isForward,
                        !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
            ……
        }
    

    ActivityThread#handleResumeActivity

    final void handleResumeActivity(IBinder token,
                boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
              ……
            r = performResumeActivity(token, clearHide, reason);
            ……
            if (r.window == null && !a.mFinished && willBeVisible) {
                    r.window = r.activity.getWindow();
                    View decor = r.window.getDecorView();
                    decor.setVisibility(View.INVISIBLE);
                    ViewManager wm = a.getWindowManager();
                    WindowManager.LayoutParams l = r.window.getAttributes();
                    a.mDecor = decor;
                    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                    l.softInputMode |= forwardBit;
                    if (r.mPreserveWindow) {
                        a.mWindowAdded = true;
                        r.mPreserveWindow = false;
                        // Normally the ViewRoot sets up callbacks with the Activity
                        // in addView->ViewRootImpl#setView. If we are instead reusing
                        // the decor view we have to notify the view root that the
                        // callbacks may have changed.
                        ViewRootImpl impl = decor.getViewRootImpl();
                        if (impl != null) {
                            impl.notifyChildRebuilt();
                        }
                    }
                    if (a.mVisibleFromClient) {
                        if (!a.mWindowAdded) {
                            a.mWindowAdded = true;
                            //注意这行代码将DecorView关联到WindowManager,而WindowManager是一个接口,真正的实现是WindowManagerImpl
                            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);
                        }
                    }
    
                // If the window has already been added, but during resume
                // we started another activity, then don't yet make the
                // window visible.
                }
    }
    

    通过注释我们知道WindowManagerImpl会将DecorView
    一般每一个handleXXX方法中都会调用一个对应的performXXX方法,内部会调用到到调用到activity的resume生命周期,我们这里不进行探究了。

    WindowManagerImpl#addView&WindowManagerGlobal#addView

    @Override
        public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
            applyDefaultToken(params);
            mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
        }
    
    
    WindowManagerGlobal#addView
    
    public void addView(View view, ViewGroup.LayoutParams params,
                Display display, Window parentWindow) {
             ……
          public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
             ViewRootImpl root;
             synchronized (mLock) {
                //我们new了一个ViewRootImpl当做root
                root = new ViewRootImpl(view.getContext(), display);
                view.setLayoutParams(wparams);
                mViews.add(view);
                mRoots.add(root);
                mParams.add(wparams);
            try {
                //注意这行代码
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                synchronized (mLock) {
                    final int index = findViewLocked(view, false);
                    if (index >= 0) {
                        removeViewLocked(index, true);
                    }
                }
                throw e;
            }
          }
       }
    

    首先我们看下ViewRootImpl是什么。


    image.png

    ViewRootImpl继承了ViewParent,然后我们来看下ViewParent家族。


    image.png
    这里显示他的另一个子类是ViewGroup。这里我们只要知道就行了,下面我会说为什么要查看ViewParent家族。

    我们看下ViewRootImpl#setView,代码比较长,我们只最追重要的看。

    ViewRootImpl#setView

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
            synchronized (this) {
                if (mView == null) {
                    mView = view;
                    //注意这行代码
                    view.assignParent(this);
                    ……
                }
            }
        }
    

    通过注释我们可以看到view.assignParent(this),而这个View就是我们上文的DecorView。至此,我们终于知道了DecorView.mParent其实就是ViewRootImpl。

    子View.mParent的真相

    我们费了千辛万苦知道了DecorView.mParent是ViewRootImpl,那么DecorView的直接子view的mParent你说应当就应该是DecorView,然后一级一级嵌套着。不过这只是我们的猜想,我们还需要验证,怎么验证,当然是再去源码中查找咯。从上文中我们知道assignParent的调用链出现在了ViewGroup#addViewInner中。

    ViewGroup#addViewInner

    image.png image.png

    多么的简单明了啊,直接把自己赋值给了子View。所以在这里我们可以直接下结论了,子View.mParent是他的父View(废话,从名字就能看出来了好吗)。

    总结

    1. DecorView的mParent为ViewRootImpl
    2. 普通View的mParent为它的父View

    每分析一次源码都会使自己更接近真相,希望大家也能自己一步步去跟一遍。 下一篇打算分析哪个模块的代码,暂时还没有想好,大家如果有好的建议的话,可以给我留言。好了,拜了个拜。大吉大利,晚上吃鸡!!!!

    相关文章

      网友评论

        本文标题:关于View中mParent的来龙去脉

        本文链接:https://www.haomeiwen.com/subject/zcswyxtx.html