一句话总结: Activity是“导演”,负责剧本(业务逻辑);Window是“舞台监制”,持有并管理舞台;DecorView是“舞台本身”,自带灯光幕布;而View则是舞台上的“演员”,负责具体表演。
一、角色分工与底层职责
| 角色 | 核心职责 | 技术本质 | 比喻 |
|---|---|---|---|
Activity | 逻辑控制器:管理生命周期和业务流转。 | 上下文(Context)提供者,UI容器的管理者。 | 导演 |
Window | 窗口抽象层:承载视图、分发事件、定义窗口属性(如背景、标题栏样式)。 | 抽象类,唯一实现是PhoneWindow。每个Activity持有一个Window实例。 | 舞台监制 |
DecorView | 视图树根节点:作为Window的具体承载体,内部划分出系统UI区域和内容区域。 | FrameLayout的子类,是Activity布局的顶层容器。 | 舞台本身 |
View | UI基本单元:负责具体的测量、布局、绘制和事件处理。 | 所有UI控件(Button, TextView等)的基类。 | 演员 |
二、从创建到显示:一条完整的调用链
Activity的界面并非一步到位,而是通过一条严谨的创建与连接链条最终呈现的。
-
Activity.attach() - 绑定监制
在Activity生命周期的早期(onCreate()之前),系统会调用attach()方法。此方法的核心工作是创建并关联一个PhoneWindow实例,自此,“导演”(Activity)就拥有了它的专属“监制”(Window)。
-
Activity.setContentView() - 演员登台
当开发者调用此方法时,看似是Activity在设置布局,实则它将任务委托给了PhoneWindow。
PhoneWindow.setContentView():首先会确保“舞台”(DecorView)已经存在。如果不存在,则会创建它。- 加载布局:使用
LayoutInflater将XML布局文件解析成一个View树。 - 添加视图:将解析出的
View树添加到DecorView内部一个ID为android.R.id.content的FrameLayout中。至此,“演员”们已经登上了舞台中央。
-
Activity.makeVisible() - 拉开帷幕(核心补充)
在onResume()之后,Activity会变得可见。此时会执行一个关键步骤:
- 创建
ViewRootImpl:WindowManager通过addView()方法为DecorView创建一个ViewRootImpl实例。ViewRootImpl是连接应用视图和系统窗口服务(WindowManagerService, WMS)的桥梁。 - 发起绘制请求:
ViewRootImpl通过IPC向WMS注册窗口,并请求进行首次绘制。 - 驱动三驾马车:
ViewRootImpl调用performTraversals()方法,这会依次触发整个View树的measure(测量)、layout(布局)和draw(绘制)流程。
- 创建
-
SurfaceFlinger - 合成显示
绘制完成后,内容被渲染到一块图形缓冲区(Surface)上,最终由系统级的SurfaceFlinger服务将所有可见的Surface合成为最终屏幕上的一帧画面。
三、思考框架之外的建议:交互与实践
1. 它们如何协同处理触摸事件?
触摸事件的传递路径完美体现了它们的职责划分:
Activity.dispatchTouchEvent():事件最先到达Activity,它负责进行拦截或分发。Window.superDispatchTouchEvent():Activity通常会直接将事件交给Window处理。DecorView.dispatchTouchEvent():Window再将事件传递给它的根视图DecorView。View树分发:最后,事件在DecorView的View树中按照我们熟知的事件分发机制(dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent)进行传递,直到找到最终的消费者。
2. 理解这个模型有什么用?
- 解释了“为何在
onCreate中无法获取View宽高” :因为在onCreate执行时,setContentView()虽然加载了布局,但DecorView还未被添加到WindowManager中,ViewRootImpl也未创建,因此performTraversals()(测量、布局、绘制)流程根本没有开始。 - 理解悬浮窗/Dialog的原理:创建一个
Dialog或PopupWindow,本质上是创建了一个新的Window(及其DecorView和View树),并将其通过WindowManager添加到屏幕上,它独立于Activity的Window。 - 实现沉浸式/全屏模式的根基:我们通过修改
Window的Flag(window.addFlags(...))或DecorView的SystemUiVisibility,来告知WindowManagerService如何调整系统UI(如状态栏、导航栏)的显示策略,从而实现全屏效果。这正是作用于“舞台监制”和“舞台本身”的最好例证。