从源码角度看Activity显示视图流程

简介

之前的《从源码角度看Activity生命周期》分析了运行在system_server进程中的AMS与运行在APP进程中的ActivityThread是如何交互、控制着Activity的生命周期的;《从源码角度看Activity的launchMode与Stack/Task》分析了Activity的launchMode不同是如何导致被启动的Activity在Task与Stack中变化的不同的

这两篇都没有侧重讲Activity与显示视图的关系,但是大多的开发者都知道Activity.onCreate中调用setContentView去设置要显示的视图,在Activity.onResume中才真正的显示视图,这中间的流程又涉及到ViewRootImpl,PhoneWindow,DecorView等等运行在APP进程中的类,同时ViewRootImpl又涉及到与WMS交互的逻辑。本篇文章我将从源码的角度带着大家看看Activity从被创建到显示视图在APP进程的这段流程

整体类图

查看大图

以上的类图涉及到了在APP进程中显示视图使用到的类:

  • ActvityThread: main函数是APP进程的入口,主要用来管理与AMS的交互
  • ActivityClientRecord: Activity在客户端的所涉及到的数据
  • Activity: 用来管理客户端视图、点击、输入等功能的抽象,具有完整的生命周期
  • PhoneWindow: 继承Window,是Activity视图管理的抽象
  • DecorView: 本质上是个FrameLayout,是Activity视图整个控件树的父节点
  • WindowManaegrImpl: 代理类
  • WindowManagerGlobal: WindowManager的实现类
  • ViewRootImpl: APP进程与system_server进程进行视图相关通信的桥梁,同时也管理着APP进程视图方面操作的主要逻辑

setContentView

Activity.setContentView

frameworks/base/core/java/android/app/Activity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 调用setContentView讲要显示的视图添加到控件树中
    setContentView(R.layout.sample);
}

public void setContentView(@LayoutRes int layoutResID) {
    // getWindow返回的是在attach方法初始化的PhoneWindow对象,attach方法是在performLaunchActivity
    // 期间、在Activity.onCreate之前被调用
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

public Window getWindow() {
    return mWindow;
}

final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
    ...
    mWindow = new PhoneWindow(this);
}

PhoneWindow.setContentView

PhoneWindow的setContentView方法有3种:

  • public void setContentView(int layoutResID): 传入layoutId, PhoneWindow将会将资源文件的视图自动解析并添加到控件树中,使用默认的LayoutParams,将占用父控件的最大空间
  • public void setContentView(View view): 直接传入View添加到控件树中,使用默认的LayoutParams,将占用父控件的最大空间
  • public void setContentView(View view, ViewGroup.LayoutParams params): 直接传入View并且指定LayoutParams控制大小

我们看看使用最频繁并且实现最复杂的 setContentView(int layoutResID)

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

@Override
public void setContentView(int layoutResID) {
    // 如果mContentParent尚未初始化,则mDecorView可能也尚未初始化
    if (mContentParent == null) {
        installDecor();
    } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        mContentParent.removeAllViews();
    }

    if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
         ...
    } else {
         // 解析视图文件,并将解析出来的View以mContentParent为父控件添加进控件树
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    ...
}

PhoneWindow.installDecor

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

private void installDecor() {
     // mDecorView是整个控件树最顶部的ViewGroup,必须确保它已经被初始化
    if (mDecor == null) {
        mDecor = generateDecor();
        ...
    }
    // mContentViewParent是Activity中内容的顶部ViewGroup,如果想要在Activity中显示视图,也必须要确保它被初始化
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
    ...
    }
    ...
}

protected DecorView generateDecor() {
     // 直接新建并返回了一个DecorView,本质上是一个继承了FrameLayout的ViewGroup
    return new DecorView(getContext(), -1);
}

    protected ViewGroup generateLayout(DecorView decor) {
        ...
        int layoutResource; 
        ...
        // 通过判断feature FLAG要决定需要解析的视图
        ...
        // 解析视图完成后,直接添加为mDecorView的子视图,并且占据了它的全部空间
       View in = mLayoutInflater.inflate(layoutResource, null);
       decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
       mContentRoot = (ViewGroup) in;
       ...
       // 最后找到id为content的ViewGroup并解析
       ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
       ...
       return contentParent;
    }

ID_ANDROID_CONTENT=content,id=content的ViewGroup实际上是layoutResource控件树中的一个子View,以下以R.layout.screen_simple为例:

```xml

<!--状态栏-->
<ViewStub android:id="@+id/action_mode_bar_stub"
top Created with Sketch.