我们在Activity.onCreate()
方法里调用setContentView()
方法后,待Activity处于RESUME状态时就会呈现视图。这一过程涉及到Activity的启动以及视图的创建和绘制,那么Activity是如何与视图(Window、View)产生联系或绑定在一起的?
概述
流程关键节点:
- 启动Activity。
Activity.startActivity()
。 - 创建窗口。Activity启动流程中会调用
ActivityThread.performLaunchActivity()
方法,该方法内部通过Activity.attach()
方法创建PhoneWindow实例,并与Activity绑定。 - 创建视图树。
ActivityThread.performLaunchActivity()
方法中待创建PhoneWindow后,接着会执行Instrumentation.callActivityOnCreate()
、Activity.onCreate()
, 最终会调用Activity.setContentView()
解析视图树并与Window绑定。 - 绘制视图。在Activity启动流程的方法
ActivityStackSupervisor.realStartActivityLocked()
中会依次添加Launch和Resume事务,待执行完ActivityThread.performLaunchActivity()
等Launch方法后,会接着执行ActivityThread.handleResumeActivity()
等Resume方法,该方法内部通过WindowManager.addView()
方法绘制视图呈现UI。
简略时序图如下:
经过上面的流程,最终在App中视图相关的各类对象间关系为:
- Activity持有PhoneWindow引用,PhoneWindow持有视图树根节点DecorView引用,DecorView对象又持有抽象根节点ViewRootImpl。
- ViewRootImpl由WindowManagerGlobal创建和管理,而它自身管理视图树的绘制。
- ViewRootImpl通过Session与WindowManagerService建立联系,由服务端管理窗口。
- Activity、PhoneWindow、DecorView、ViewRootImpl、Session和WindowState是一一对应关系。
- WindowManagerGlobal为单例,App进程全局唯一。
上面的箭头表示有联系,直接持有引用或间接联系。
启动Activity
Activity的启动流程见我的博客:Activity启动流程概览
创建窗口
在Activity.attach()
方法创建PhoneWindow实例:
1 | final void attach(Context context, ActivityThread aThread ...) { |
PhoneWindow对象实例化时,先判断是否有缓存window时,有就直接绑定之前的DecorView,DecorView是视图树的根节点;没有缓存window则会在之后的Activity.setContentView()
时绑定DecorView。
创建视图树
概述
在Activity.setContentView(@LayoutRes int layoutResID)
中解析并创建视图树。在PhoneWindow中创建DecorView实例,利用LayoutInflater解析布局文件。主要流程:
- 生成
DecorView
(继承FrameLayout)实例。 - 解析
screen_*.xml
布局(包含ID为content的FrameLayout,对应引用为mContentParent
)并填充到DecorView
中。 - 解析App自定义的布局文件并填充到
mContentParent
引用的FrameLayout中。
简略时序图:
最终生成的视图树结构:
screen_*.xml
文件位于base/core/res/res/layout
目录下,screen_titil.xml
示例:
1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
关键方法
Activity.setContentView()
1 | public void setContentView(int layoutResID) { |
PhoneWindow.setContentView()
1 | public void setContentView(int layoutResID) { |
PhoneWindow.generateLayout()
填充App内容的父布局,一般包括标题区域和内容区域,内容区域的视图ID固定为ID_ANDROID_CONTENT = "com.android.internal.R.id.content"
。
1 | protected ViewGroup generateLayout(DecorView decor) { |
LayoutInflater.inflate()
解析内容布局文件生成视图树并添加到内容视图容器(ID_ANDROID_CONTENT
)里。
1 | public View inflate(XmlPullParser parser, boolean attachToRoot) ViewGroup root, { |
绘制视图
概述
视图绘制的入口为ActivityThread.handleResumeActivity()
,该方法主要工作为:
- 添加Window。调用方法中的
WindowManager.addView()
创建ViewRootImpl
,与DecorView
绑定。在ViewRootImpl
中创建Window
,并将该Window
通过Session
添加到WMS
里,其代表为WindowState
。在此过程WindowState
会创建与SurfaceFlinger
的连接SurfaceSession
(Java层)和SurfaceComposerClient
(Native层),只有与SurfaceFlinger连接后绘制的视图才能渲染呈现出来。 - 执行具体绘制操作。与WindowManagerService建立连接的前、后都会去执行绘制操作,连接前调用
ViewRootImpl.requestLayout()
,连接后调用Activity.makeVisible()
(该方法是否会被调用?TODO),但无论哪个绘制入口,最终都会调用ViewRootImpl.scheduleTraversals()
。
本节只讨论添加Window的操作,不讨论具体View绘制以及与Surface、SurfaceFlinger相关的流程,具体绘制流程见我的相关博客。
本节WMS与Window逻辑撰写不够清晰,如待以后更新 TODO。
时序图:
关键类
这块涉及到的类比较多和繁杂,有必要单独详细说明一下。类图如下:
App端:
- Window。用户与App交互的顶级视图,可理解为具体视图View的容器,与底层的Surface一一对应,多个Window或多个Surface最终形成一帧图像呈现给用户。
- PhoneWindow,继承Window。Activity持有窗口实例,持有成员:DecorView、状态栏各属性、主题、导航和标题等属性等。
- ViewRootImpl,继承ViewParent。App端视图树抽象的根,但不是实际的View,通过Session与服务端通信,并管理视图树的具体绘制。
- DecorView,继承FramLayout。窗口实际视图树的根View,持有ViewRootImpl作为父。
- WindowManager,继承ViewManger接口。包含View的添加、更新和移除等操作方法。
- WindowManagerImpl,实现WM。WMImp持有WMGlobal单例,WMImpl在生成实例时会生成全局(应用)唯一的WMGlobal。
- WindowManagerGlobal,单例。提供与WMS低级别通信的方法,与Context无关,持有成员:DecorView集合、ViewRootImpl集合以及布局参数集合等。
Service端:
- ViewRootImpl.W,实现IWindow。客户端与服务端通信时代表客户端的窗口,持有成员:ViewRootImpl和Session。这个与App端的Window不是同一个东西,是它的代表。
- Session,实现IWindowSession。用于App端与服务端间的通信,每个App端都有一个Session与服务端连接。
- WMS,实现IWindowManager。主要用于管理App端的窗口:为窗口分配Surface,创建与SurfaceFlinger的连接,掌管Surface的显示顺序(Z序)以及大小尺寸、控制窗口动画,另外还管理输入事件的通道。
- WindowState,实现WMPolicy.WindowState。用于管理IWindow,持有成员:IWindow、Session等。
关键方法
ActivityThread.handleResumeActivity()
主要工作:执行Resume并最终执行Activity.onResume()方法、添加Window、执行绘制
1 | public void handleResumeActivity(IBinder token...) { |
WindowManagerGlobal.addView()
创建ViewRootImpl
1 | // android.view.WindowManagerGlobal |
ViewRootImpl.setView()
1 | public void setView(View view, WindowManager.LayoutParams attrs, ...) { |
WindowManagerService.addWindow()
创建WindowState、SurfaceSession等
1 | public int addWindow(Session session, IWindow client, int seq,...){ |