PhoneWindow、DecorView 和 ViewRootImpl 的关系与工作原理
一、整体架构与对应关系
1.1 组件层级结构
Application
└── Activity (N个)
└── PhoneWindow (1:1)
└── DecorView (1:1)
└── ContentView (setContentView 设置的布局)
└── TitleBar/ActionBar
└── ViewRootImpl (1:1)
1.2 对应关系表
| 组件 | 对应关系 | 数量关系 | 说明 |
|---|---|---|---|
| ViewRootImpl ↔ 应用 | ❌ 不一对一 | N:1 | 一个应用可有多个 ViewRootImpl |
| ViewRootImpl ↔ Window | ✅ 一对一 | 1:1 | 每个 Window 都有一个 ViewRootImpl |
| PhoneWindow ↔ Activity | ✅ 一对一 | 1:1 | 每个 Activity 创建一个 PhoneWindow |
| DecorView ↔ PhoneWindow | ✅ 一对一 | 1:1 | 每个 PhoneWindow 有一个 DecorView |
| ViewRootImpl ↔ DecorView | ✅ 一对一 | 1:1 | ViewRootImpl 管理 DecorView |
二、各组件核心职责
2.1 PhoneWindow
// Activity.attach() 中创建
mWindow = new PhoneWindow(this);
特点:
- 实现
Window接口,属于android.policy包 - 提供
setContentView()、setFeatureInt()等 API - 管理窗口特性:flags、features(全屏、悬浮窗等)
职责:
- 作为 Activity 与窗口系统的桥梁
- 管理窗口样式和行为
- 提供 Activity 回调接口的代理
2.2 DecorView
// PhoneWindow 内部创建
private DecorView mDecor;
特点:
- 继承自
FrameLayout,是窗口的根视图 - 在
setContentView()阶段就创建完成 - 包含系统 UI 和用户内容
组成结构:
DecorView (FrameLayout)
├── TitleBar / ActionBar(如有)
├── 系统状态栏占位
└── android.R.id.content (FrameLayout - 用户内容容器)
└── setContentView() 设置的布局
2.3 ViewRootImpl
// WindowManagerGlobal.addView() 中创建
root = new ViewRootImpl(view.getContext(), display);
特点:
- 不是 View,是 ViewTree 的管理者
- 在
onResume()阶段才创建 - 是系统的真正入口点
核心职责:
- 三大流程:测量(measure) → 布局 → 绘制
- 与 WMS 通信:申请 Surface,处理系统窗口事务
- 输入事件:通过 InputChannel 接收并分发事件
- 渲染同步:通过 Choreographer 控制帧率(60fps/120fps)
- 脏区域优化:仅重绘变化区域
- 焦点管理:管理 View 树的焦点切换
三、完整时序:三个关键阶段
3.1 阶段概览
┌─────────────────────────────────────────────────────────────────┐
│ 阶段1: attach() 创建 Window │
└─────────────────────────────────────────────────────────────────┘
Activity.attach()
↓
mWindow = new PhoneWindow(this) // ✅ PhoneWindow 已创建
↓
mWindow.setCallback(this) // Activity 实现了 Window.Callback
┌─────────────────────────────────────────────────────────────────┐
│ 阶段2: onCreate() 设置内容 │
└─────────────────────────────────────────────────────────────────┘
Activity.onCreate()
↓
setContentView(R.layout.activity_main)
↓
PhoneWindow.setContentView()
↓
installDecor() → mDecor = new DecorView() // ✅ DecorView 已创建
↓
mDecor.addView(contentView) // 用户布局添加进来了
┌─────────────────────────────────────────────────────────────────┐
│ 阶段3: onResume() 添加到 WMS │
└─────────────────────────────────────────────────────────────────┘
Activity.onResume()
↓
Activity.makeVisible()
↓
WindowManager.addView(mDecor) // 传入 DecorView
↓
WindowManagerGlobal.addView()
↓
root = new ViewRootImpl(...) // ✅ ViewRootImpl 终于创建了!
↓
root.setView(mDecor) // ViewRootImpl 持有 DecorView
↓
requestLayout() → scheduleTraversals() // 开始三大流程
3.2 各阶段状态对比
| 组件 | 阶段1:attach() | 阶段2:onCreate() | 阶段3:onResume() |
|---|---|---|---|
| PhoneWindow | ✅ 已创建 | ✅ 存在 | ✅ 存在 |
| DecorView | ❌ 未创建 | ✅ 已创建 | ✅ 存在 |
| ViewRootImpl | ❌ 未创建 | ❌ 未创建 | ✅ 已创建 |
| WMS 窗口 | ❌ 未注册 | ❌ 未注册 | ✅ 已注册 |
| Surface | ❌ 无 | ❌ 无 | ✅ 有 |
| 输入事件 | ❌ 无 | ❌ 无 | ✅ 正常 |
四、各阶段详细解析
4.1 阶段1:attach() - PhoneWindow 创建
// ActivityThread.java
private void performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity a = performCreateActivity(r, customIntent);
// attach() 就在这里
if (a != null) {
a.attach(...);
}
}
// Activity.java
final void attach(Context context, ActivityThread aThread, ...) {
attachBaseContext(context);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
// ...
}
此时 PhoneWindow 的状态:
- ✅ 已创建,纯数据结构存在
- ❌ 没有对应的 WMS 窗口
- ❌ 没有 Surface
- ❌ 不参与渲染
- ❌ 不处理输入事件
Window.Callback 的作用:
// PhoneWindow 中的回调定义
public interface Callback {
boolean dispatchKeyEvent(KeyEvent event);
boolean dispatchTouchEvent(MotionEvent event);
boolean trackMotionEvent(MotionEvent event);
void onWindowAttributesChanged(WindowManager.LayoutParams attrs);
void onContentChanged();
void onWindowFocusChanged(boolean hasFocus);
void onAttachedToWindow();
void onDetachedFromWindow();
void onPanelClosed(int featureId, Menu menu);
// ...
}
Activity 实现了这些方法,成为窗口的回调处理器:
onBackPressed()← Callback 触发onConfigurationChanged()← Configuration 变化onKeyDown()← 按键事件
4.2 阶段2:onCreate() - DecorView 创建
// PhoneWindow.java
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); // 创建 DecorView
}
// ...
mLayoutInflater.inflate(layoutResID, mContentParent);
}
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1); // ✅ DecorView 创建了
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); // 加载系统布局
}
// ...
}
此时 DecorView 的状态:
- ✅ View 树已完整
- ✅ 包含系统 UI(TitleBar/ActionBar 等)
- ✅ 用户布局已加载
- ❌ 但还没有 ViewRootImpl 管理
- ❌ 不响应输入事件
- ❌ 不会 requestLayout()
- ❌ 不会触发渲染
// DecorView 根据不同 features 加载不同布局
protected ViewGroup generateLayout(DecorView decor) {
// ...
// 基础布局
layoutResource = R.layout.screen_simple;
// 有 Action Bar
if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = R.layout.screen_action_bar;
}
// 全屏模式
if ((features & (1 << FEATURE_NO_TITLE)) != 0) {
layoutResource = R.layout.screen_no_title;
}
// 沉浸式
if ((features & (1 << FEATURE_FULLSCREEN)) != 0) {
layoutResource = R.layout.screen_fullscreen;
}
// ...
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// ...
}
4.3 阶段3:onResume() - ViewRootImpl 创建与连接
// Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE); // 显示
}
// WindowManagerImpl.java - 代理类
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
// WindowManagerGlobal.java - 真正的实现
public void addView(View view, ViewGroup.LayoutParams params, Display display) {
// ViewRootImpl 构造时就会与 WMS 通信
root = new ViewRootImpl(view.getContext(), display);
// 关键:这里才会请求 WMS 创建窗口
res = mWindowSession.addToDisplay(...);
// 设置 ViewRootImpl 与 DecorView 的关系
root.setView(view, wparams, panelParentView);
}
ViewRootImpl.setView() 的关键操作:
// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view; // 持有 DecorView
// 1. 设置 AttachInfo,传入 this (ViewRootImpl)
view.assignParent(this);
// 2. 请求 WMS 添加窗口
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mRequestedConfiguration, mInputChannel);
// 3. 创建 InputChannel,开始接收事件
if (mInputChannel != null) {
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
}
// 4. 首次触发三流程
requestLayout();
}
}
}
此时 ViewRootImpl 的状态:
- ✅ 持有 DecorView
- ✅ 已向 WMS 注册窗口
- ✅ 已获得 Surface
- ✅ 已建立 InputChannel
- ✅ 开始响应渲染循环
五、为什么 ViewRootImpl 这么晚才创建?
5.1 原因1:WMS 通信成本
// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params, Display display) {
// ViewRootImpl 构造时就会与 WMS 通信
root = new ViewRootImpl(view.getContext(), display);
// 关键:这里才会请求 WMS 创建窗口
res = mWindowSession.addToDisplay(...);
}
WMS 通信的 Binder 调用开销较大,只有在真正要显示时才创建相关资源。
5.2 原因2:生命周期控制
// Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
- Activity 在
onPause()、onStop()时 DecorView 可能 detach - 但 Activity 返回时需要复用,ViewRootImpl 重建即可
- PhoneWindow 和 DecorView 可以保持不变,减少对象创建
5.3 资源按需分配
| 资源 | 分配时机 | 原因 |
|---|---|---|
| PhoneWindow | attach() | 数据层,轻量级,正常分配 |
| DecorView | setContentView() | View 树初始化,必须要有 |
| ViewRootImpl | onResume() | 需要 Surface、WMS、InputChannel,按需创建 |
| Surface | ViewRootImpl 创建后 | GPU/系统资源,最后分配 |
六、ViewRootImpl 的扩展能力
6.1 Surface 控制
// 通过 mSurface 与 WMS 通信,申请渲染 Surface
mSurface = new Surface();
mSurface.copyFrom(mWindowSession.getSurface(mWindow));
- 负责与 WMS 协商创建图形缓冲区
- 处理 Surface 的创建、销毁、大小的变更
6.2 硬件加速控制
// 控制是否开启硬件渲染
mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent);
- 配置硬件加速
- 管理硬件渲染管道
6.3 输入通道管理
// 创建 InputChannel,通过 Looper 接收输入事件
mInputChannel = new InputChannel();
mWindowSession.openInputChannel(...);
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
- 负责与 InputManagerService 建立通信
- 接收按键、触摸等输入事件并分发到 DecorView
6.4 动画与同步控制
// 通过 Choreographer 控制帧率
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
- 使用 Choreographer 与垂直同步信号同步
- 支持 60fps / 90fps / 120fps 等不同刷新率
6.5 脏区域优化
// 仅重绘变化区域
private final Rect mDirty = new Rect();
void invalidateRectOnScreen(Rect dirty) {
// 合并脏区域
mDirty.union(dirty);
// 仅重绘脏区域
scheduleTraversals();
}
- 只重绘需要更新的区域,提升性能
- 避免全屏刷新的开销
6.6 焦点管理
// 管理 View 树的焦点切换
View findFocus() {
return mView.findFocus();
}
void requestFocus(View focused, int direction) {
// 处理焦点转移逻辑
}
- 管理整个 View 树的焦点状态
- 处理焦点的获取和失去
- 支持键盘导航的方向控制
七、时间轴全景图
Activity 启动完整时间轴
onCreate() onStart() onResume() ────────────→ 窗口可见
│ │ │
├─ attach() │ ├─ makeVisible()
│ └─ new PhoneWindow() │ │ └─ WindowManager.addView()
│ ├─ 构造完成 │ │ └─ new ViewRootImpl()
│ └─ setCallback(this) │ │ ├─ 构造完成
│ │ │ └─ setView(mDecor)
├─ setContentView() │ │ ├─ assignParent(this)
│ └─ installDecor() │ │ └─ addToDisplay(WMS)
│ ├─ new DecorView() │ │ ├─ 创建窗口
│ ├─ generateLayout() │ │ └─ 创建 InputChannel
│ └─ inflate 用户布局 │ │
│ │ ├─ requestLayout()
│ │ │ └─ scheduleTraversals()
│ │ │ └─ Choreographer.postCallback()
│ │ │ └─ performTraversals()
│ │ │ ├─ measure()
│ │ │ ├─ layout()
│ │ │ └─ draw()
│ │ │ └─ mSurface.lockCanvas()
│ │ │ └─ mSurface.unlockCanvasAndPost()
│ │ │
[数据存在] [数据存在] [真正显示]
[无 WMS 窗口] [无 WMS 窗口] [有 WMS 窗口]
[无 Surface] [无 Surface] [有 Surface]
[无输入通道] [无输入通道] [有输入通道]
[不会渲染] [不会渲染] [开始渲染]
八、关键问题解答
Q1: 为什么在 setContentView() 后立即访问 view 有时是 null?
因为 ViewRootImpl 还没接管:
- DecorView 已创建,但还没渲染
- View 的状态可能未初始化完全
- 视口尺寸可能还是 0
Q2: 多个 Activity 如何共享资源?
| 资源 | 是否共享 | 说明 |
|---|---|---|
| Application | ✅ | 全局单例 |
| PhoneWindow | ❌ | 每个 Activity 独立 |
| DecorView | ❌ | 每个 Activity 独立 |
| ViewRootImpl | ❌ | 每个 Activity 独立 |
| Surface | ❌ | 每个 Window 独立 |
| WindowService | ✅ | 系统服务共享 |
Q3: Dialog 的 ViewRootImpl 是什么?
// Dialog 创建时会创建一个独立的 ViewRootImpl
mWindowManager.addView(mDecor, layoutParams);
- Dialog 拥有独立的 Window 和 ViewRootImpl
- 这就是为什么 Dialog 可以上层显示
- Dialog 的 ViewRootImpl 脱离 Activity 的控制
Q4: 如何调试 ViewRootImpl 相关问题?
// 在 ViewRootImpl 中有 Log 开关
if (DEBUG_LAYOUT) {
Log.v(TAG, "measuring...");
}
// 启用 Layout Inspector 查看真实层级
// 使用 GPU 过渡模式绘制查看重绘区域
// 使用 Systrace 查看帧率和性能
九、总结
-
三个组件的生命周期不同:
- PhoneWindow:attach() → 存在整生命周期
- DecorView:setContentView() → 存在整生命周期
- ViewRootImpl:onResume() → 动态创建/销毁
-
View 树与 窗口系统的连接点:
- ViewRootImpl 是连接 Android View 系统与底层窗口系统的桥梁
- 没有它,View 树只是数据结构,无法显示
-
资源分配的层次化设计:
- 数据层(PhoneWindow、DecorView)→ 轻量级,优先创建
- 连接层(ViewRootImpl)→ 中量级,按需创建
- 系统资源(Surface、WMS)→ 重量级,最后分配
-
生命周期管理的关键:
- Activity 的 stop/start 可以复用 ViewTree
- 只需重建 ViewRootImpl 即可
- 这是性能优化的核心思想之一
这种设计既保证了用户体验(快速响应),又优化了系统资源使用(按需分配),是 Android 框架的经典设计模式之一。