最近阅读Android源码,似乎有点发现新大陆的感觉。以前经常接触Android知识,在阅读源码中,开始变得豁然开朗。前两天才写完两篇博文Activity启动流程源码分析(应用中)和Activity启动流程源码分析(Launcher中),今天,就急不可耐的想写写Activity布局加载流程,其实,也就是想趁热打铁,好好梳理梳理这部分知识。
在开始梳理之前,我们需要了解一些概念,如:
- Window: 是一个抽象类,表示是一个窗口。Android系统中的界面,也都是以窗口的形式存在的。
- PhoneWindow: 是Window类具体实现类,Activity中布局加载逻辑主要就是在此类中完成的。
- WindowManager: 是Window的管理类,管理着Window的添加、更新和删除。
- WindowManagerService(AMS):是系统窗口管理服务类,具体管理着系统各种各样的Window.
- DecorView:是Window的顶级View,主要负责装载各种View。
一、Activity布局加载分析
我们知道,设置Activity布局内容,主要是在Activity的onCreate()中调用setContentView()方法,下面让我们来看看此方法
这里主要调用了getWindow().setContentView()方法,我们来看看Activity中getWindow()
由此知mWindow是Activity一个属性变量,在前面Activity启动流程介绍中,我们知道在Activity启动前都会先调用attach(),而这mWindow就是在attach初始化的时候赋值的,我们来看看Activity的attach源码
这里我们来关注一下PolicyManager.makeNewWindow(this)方法,创建Window,我们来看看PolicyManager类
由上易知,这里主要是通过反射初始化Policy,然后利用设计模式里氏替换原则调用Policy的makeNewWindow()方法,我们继续来看Policy中的方法
我们可以发现mWindow其实就是PhoneWindow,在Activity中getWindow().setContentView()方法,就是调用PhoneWindow中的setContentView方法,所以我们这里来看看PhoneWindow中的setContentView()方法
从注释2,我们知道布局填充器mLayoutInflater向mContentParent填充我们的布局内容,而mContentParent是一个ViewGroup,它是怎么赋值的呢?这里我们要来看注释1,当mContentParent为空时,会安装装饰器,我们继续来看phoneWindow中installDecor()方法
首先,我们先来看看注释1装饰器的生成方法generateDecor()
这里主要就是装饰器DecorView的初始化,我们再来看一下DecorView的源码
DecorView类包括内容还有许多,这里就不介绍了,我们只需知道DecorView是PhoneWindow的内部类,DecorView继承于FrameLayout,实现RootViewSurfaceTaker接口。下面我们来看一下mContentParent的生成,即generateLayout(mDecor)方法
这里我们来看一下,系统默认的几种Activity的头部布局xml文件,布局文件的源码位置为:android4.1.1_r1\frameworks\base\core\res\res\layout,我们挑两个文件来看一下:
第一个screen_title.xml
第二个,screen_simple.xml
通过对比,我们发现这两个布局文件都有一个共同id为@android:id/content的FrameLayout,其实这也就是我们Activity布局填充容器。我们还发现,这两个布局父布局都是一个线性布局LinearLayout,并且方向都是垂直的,这也验证了我们Activity内容布局一般都是状态栏的下边的模式。我们再来看后面的代码
这里向装饰器添加了系统布局View,并从系统布局View中获取了Activity填充内容的容器ViewGroup。其中ID_ANDROID_CONTENT就是com.android.internal.R.id.content,通过(ViewGroup)findViewById(ID_ANDROID_CONTENT)就获取了布局文件中的FrameLayout,即Activity内容填充布局的ViewGroup。这样我们再回到PhoneWindow的setContentView方法
现在mContentParent已经赋完值了,再通过布局填充器mLayoutInflater的inflate()方法,这样我们就把Activity的布局文件添加到装饰器上了。然而,现在虽然装饰器DecorView上已经有了Activity布局内容,但是是什么时候添加到Window上的呢?这里就需要了解Activity的启动流程,在Activity的启动流程最后几步会执行ActivityThread中handleLaunchActivity()方法,我们接着此方法继续分析
在注释1处,已经建立Activity的实例,并且执行Activity生命周期的attach()和onCreate()方法。我们知道setContentView()也就在onCreate()方法中调用的,所以这个时候,我们Activity布局文件内容已经装入了装饰器DecorView中,接下来就是把DecorView和Window关联起来,所以下面我们继续来看handleResumeActivity()方法
在注释1处,调用performResumeActivity(token, clearHide)方法,实际上就是调用activity生命周期的onResume()方法。注释2处,通过Binder跨进程通信,调用ActivityManagerService中willActivityBeVisible()获取显示Activity的控制开关,从而在注释3处,通过WindowManager添加装饰器DecorView到Window,然后,再调用相关View的绘制流程,这样一个有布局的Activity就被加载出来了。
到这里,Activity布局加载流程就是梳理完了。
注:源码采用android-4.1.1_r1版本,建议下载源码然后自己走一遍流程,这样更能加深理解。