WMS-01-setContentView的实例化与PhoneWindow的addView

image.png

一、我们要知道的几个点

    1. 桌面点击某个应用后的启动流程?
    1. setContentView流程,系统的布局,应用内的布局view如何准备和实例化?
    1. 2中的View数据实例化好之后交给谁?

二、 首先我们知道了App启动流程

  1. Launcher.app --> 点击应用图标 --> 与AMS进行binder通信,告知请求 --> AMS与Zygote通过Socket通信 --> Zygote进程fork出一个App进程

  2. ActivityThread.main方法中thread.attach 来通过和AMS通信实例化和启动Application实例。

  3. 启动Application实例后,紧接着开始实例和启动main Activity。最终会调到ActivityStackSupervisor.realStartActivityLocked方法。它通过添加ClientTransaction事务的方式回调ActivityThread。ClientTransaction中会添加Item:它们会分别处理Activity不同生命周期回调(LaunchActivityItem、StartActivityItem、ResumeActivityItem、PauseActivityItem、StopActivityItem)。它们分别对应ActivityThread中的handleLaunchActivity、handleStartActivity、handleResumeActivity、handlePauseActivity、handleStopActivity。跟踪它们就能看到Activity中不同生命周期方法是如何调起的。

  4. TransactionExecutor中的execute方法中 executeCallbacks(transaction);和executeLifecycleState(transaction);就是上一个生命周期调用下一个生命周期的逻辑。

三、setContentView流程,系统的布局,应用内的布局view如何准备和实例化?

看setContentView有两种,一种MainActivity继承至Activity;一种是MainActivity继承至AppCompatActivity;

3.1 AppCompatActivity这种的话,最终是到AppCompatDelegateImpl的setContentView方法
    @Override
    public void setContentView(View v) {
        ensureSubDecor();
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        contentParent.removeAllViews();
        contentParent.addView(v);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }

在setContentView里,有ensureSubDecor方法,因为AppCompatActivity是androidx包下的,在ensureSubDecor中就开始了狸猫换太子之旅,它将自己的xml布局换成了系统的android.R.id.content。

...
        final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
                R.id.action_bar_activity_content);

        final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
        if (windowContentView != null) {
            // There might be Views already added to the Window's content view so we need to
            // migrate them to our content view
            while (windowContentView.getChildCount() > 0) {
                final View child = windowContentView.getChildAt(0);
                windowContentView.removeViewAt(0);
                contentView.addView(child);
            }

            // Change our content FrameLayout to use the android.R.id.content id.
            // Useful for fragments.
            windowContentView.setId(View.NO_ID);
            contentView.setId(android.R.id.content);

            // The decorContent may have a foreground drawable set (windowContentOverlay).
            // Remove this as we handle it ourselves
            if (windowContentView instanceof FrameLayout) {
                ((FrameLayout) windowContentView).setForeground(null);
            }
        }

        // Now set the Window's content view with the decor
        mWindow.setContentView(subDecor);
...
3.2 上面的AppCompatActivity就可以结束了,接着着重看Activity中的setContentView()
  • 3.2.1 Activity.setContentView实际调用的是PhoneWindow的setContentView
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
  • 3.2.2 在PhoneWindow中的setContentView就是根据App所选择的系统样式获得系统的根布局,然后在把传过来的App自己的View解析反射实例化,add到mContentParent中去,下面分别说明
    public void setContentView(int layoutResID) {
       
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        ...
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }
  • 3.2.3 installDecor()方法先获取系统配置的布局样式,得到App能显示的mContentParent区域。
image.png
  • 3.2.3.1 首先,mDecor为空的话,则创建一个DecorView。mDecorView实际上就是一个FrameLayout
private void installDecor() {
    mForceDecorInstall = false;
    if (mDecor == null) {
        mDecor = generateDecor(-1);
        ...
    } 
    
   if (mContentParent == null) {
       mContentParent = generateLayout(mDecor);
   }

   ...
   //后面没啥了,基本就是mContentParent一些属性相关的设置
}
  • 3.2.3.2 然后,就开始通过generateLayout方法来给mContentParent赋值。它根据不同的Feature来选择不同的xml布局,如你选择的Activity是有title的、无title等。这里我们看到R.layout.screent_simple。(一般在sdk目录/platforms/选择不同的SDK版本/data/res/layout里以screen为头的xml)。在screen_simple.xm中我们可以看到熟悉的“@android:id/content”,这里就是我们自己Activity布局所要显示的父控件。
protected ViewGroup generateLayout(DecorView decor) {
    ...
    //直接跳到根据Feature来选择不同xml布局来
    int layoutResource;
    int features = getLocalFeatures();
    
    if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
          
        layoutResource = R.layout.screen_title_icons;
        ...
    } else {
        layoutResource = R.layout.screen_simple;
    }

    //将选择好的layout的id交由DecorView去按层级解析,反射实例化并一层层addView绑定
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

    ...
    //找到id为android.R.id.content的控件
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
}

    ...
    //将contentParent返回
    return contentParent;

screen_simple.xml布局


    
    

  • 3.2.4 PhoneWindow的setContentView方法,初始化DecorView和mContentParent之后,就会将传入的App的Activity的layoutResID,使用LayoutInflater.inflate()来进行解析,解析XML、反射实例化View、并与mContentLayout按层级进行addView.
    public void setContentView(int layoutResID) {
      
        if (mContentParent == null) {
            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);
        }
        ...
    }

四、setContentView基本先找到系统的XML布局,实例化。然后在将自己的xml实例化绑定到mContentLayout中,但是到这一步貌似onCreate已经J结束了。那下一步去哪呢?我们下一步根据Activit生命周期方法,应该到了onResume了。

4.1 在App启动流程中的TransactionExcuter.execute中我们提过,Activity上一个生命周期方法执行完,就调用下一个生命周期方法。如下executeLifecycleState()方法。onCreate执行完后,其实是执行ResumeActivityItem中的execute方法。它会回调ActivityThread中的handleResumeActivity方法。
public void execute(ClientTransaction transaction) {
        ...
        executeCallbacks(transaction);

        executeLifecycleState(transaction);
       
    }
4.2 在handleResumeActivity方法中有一个地方值得注意wm.addView(decor, l),它将我们初始化好的DecorView加入到了wm中,这个wm就是performLaunchActivity方法activity.attach()里实例化的mWindowManager,它实际上是WindowManagerImpl对象。
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String 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;
            ...
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                } else {
                   ...
                }
            }

            ...
    }
4.3 在WindowManagerImpl.addView方法中,实际上调用的是mGlobal.addView方法,而mGlobal实际上是WindowManagerGlobal对象。
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }
4.4 进入到WindowManagerGlobal的addView方法,它会实例化一个ViewRootImpl对象,然后调用它的setView()方法。(ViewRootImpl用来管理所有的view的绘制策略,你怎么绘制由这个类管理)
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
    ViewRootImpl root;
    synchronized (mLock) {
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);

        try {
            root.setView(view, wparams, panelParentView, userId);
        } catch (RuntimeException e) {
                
        }
    }
}

五、后续在跟进看编舞者类如何对view进行绘制

你可能感兴趣的:(WMS-01-setContentView的实例化与PhoneWindow的addView)