从源码角度看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.