PhoneWindow、DecorView 和 ViewRootImpl 的关系与工作原理

4 阅读7分钟

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:1ViewRootImpl 管理 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 资源按需分配

资源分配时机原因
PhoneWindowattach()数据层,轻量级,正常分配
DecorViewsetContentView()View 树初始化,必须要有
ViewRootImplonResume()需要 Surface、WMS、InputChannel,按需创建
SurfaceViewRootImpl 创建后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 查看帧率和性能

九、总结

  1. 三个组件的生命周期不同:

    • PhoneWindow:attach() → 存在整生命周期
    • DecorView:setContentView() → 存在整生命周期
    • ViewRootImpl:onResume() → 动态创建/销毁
  2. View 树与 窗口系统的连接点:

    • ViewRootImpl 是连接 Android View 系统与底层窗口系统的桥梁
    • 没有它,View 树只是数据结构,无法显示
  3. 资源分配的层次化设计:

    • 数据层(PhoneWindow、DecorView)→ 轻量级,优先创建
    • 连接层(ViewRootImpl)→ 中量级,按需创建
    • 系统资源(Surface、WMS)→ 重量级,最后分配
  4. 生命周期管理的关键:

    • Activity 的 stop/start 可以复用 ViewTree
    • 只需重建 ViewRootImpl 即可
    • 这是性能优化的核心思想之一

这种设计既保证了用户体验(快速响应),又优化了系统资源使用(按需分配),是 Android 框架的经典设计模式之一。