Android 中的 PhoneWindow 和 DecorView 是窗口系统里非常核心的两个类,它们一起承担了"一个窗口如何显示到屏幕上"的职责。下面从概念、职责、结构、协作流程几个方面详细说明。
1. Window 体系概览
在 Android 中,一个 Window 是一个抽象概念,代表屏幕上的一块矩形区域,用于承载视图并处理窗口行为。每个 Activity 或 Dialog、PopupWindow 等都会持有一个 Window。
核心类关系:
-
Window:抽象类,定义窗口的基本 API,如setContentView、setTitle、setStatusBarColor等。 -
PhoneWindow:Window的唯一具体实现类,负责窗口的构建和管理。 -
DecorView:PhoneWindow内部的根布局,是真正的顶层View。 -
ViewRootImpl:负责连接DecorView和WindowManagerService(WMS),执行测量、布局、绘制、事件分发。
可以简单理解为:
Window是抽象窗口描述,PhoneWindow是窗口的实现,DecorView是窗口的根视图,ViewRootImpl是窗口与系统服务之间的桥梁。
2. PhoneWindow 的作用
PhoneWindow 位于 frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java,是 Window 的实现类。
2.1 窗口内容的容器管理者
PhoneWindow 负责创建和管理 DecorView,并把用户设置的布局添加到 DecorView 的 content 区域中。
主要职责包括:
-
创建 DecorView
通过
installDecor()方法生成DecorView,DecorView是窗口的根布局。 -
加载窗口布局模板
根据窗口的
features(如是否有标题、是否有 ActionBar、是否全屏等)选择不同的布局模板,例如:-
R.layout.screen_simple(无标题栏) -
R.layout.screen_title(有标题栏) -
R.layout.screen_action_bar(有 ActionBar) -
R.layout.screen_toolbar(有 Toolbar)
-
-
确定内容区域
在模板中有一个
ID_ANDROID_CONTENT的FrameLayout,通常命名为content,用户通过setContentView()设置的布局最终会被添加到这里。 -
设置窗口属性
维护窗口的宽高、软键盘模式、状态栏颜色、导航栏颜色、背景、转场动画等属性,并传递给
WindowManager.LayoutParams。 -
菜单、标题、ActionBar 管理
PhoneWindow负责处理setTitle、setActionBar、setStatusBarColor等逻辑,并将这些变化反映到DecorView中。 -
回调处理
实现了
Window.Callback接口(例如Activity实现了该接口),将系统事件(如菜单创建、按键事件)分发给当前Activity。
3. DecorView 的作用
DecorView 是 PhoneWindow 的内部类,继承自 FrameLayout,是窗口的根视图,也是整个 View 层级的最顶层节点。
3.1 根视图角色
DecorView 是唯一直接添加到 WindowManager 的 View。
WindowManagerGlobal.addView() 会将 DecorView 和 ViewRootImpl 关联,之后由 ViewRootImpl 负责测量、布局、绘制以及事件分发。
也就是说:
用户自己写的布局不是直接挂在窗口上的,而是挂在
DecorView的content子容器中。
3.2 结构组成
DecorView 内部通常包含以下几部分:
text
DecorView (FrameLayout)
├── 状态栏背景 View(statusBarBackground)
├── 导航栏背景 View(navigationBarBackground)
├── 标题栏/ ActionBar 容器(可能)
└── content(ID_ANDROID_CONTENT,用户布局最终添加到这里)
不同版本的 Android 和不同的窗口特性会加载不同的布局模板,但核心都类似:系统需要显示的背景(状态栏、导航栏)和用户内容区域。
3.3 处理系统窗口 Insets
DecorView 负责处理 WindowInsets(系统栏的尺寸、刘海区域、手势区域等),并根据这些 insets 调整内容区域的 padding 或位置,避免内容被状态栏、导航栏遮挡。
例如:
-
setStatusBarColor()会改变DecorView中状态栏背景的颜色。 -
setSystemUiVisibility()会影响DecorView的systemUiVisibility标志。
3.4 事件分发
由于 DecorView 是根 View,所有触摸事件、按键事件都会首先经过它,然后通过 Window.Callback 分发给 Activity 或子 View。
3.5 窗口背景绘制
DecorView 负责绘制窗口的背景(例如 windowBackground 属性指定的 drawable),保证整个窗口区域有正确的背景。
4. 层级关系总结
可以用下面的图来表示它们之间的关系:
Activity
└── PhoneWindow (Window 唯一实现)
├── DecorView (根 View,继承 FrameLayout)
│ ├── statusBarBackground
│ ├── navigationBarBackground
│ ├── actionBar / title
│ └── content (ID_ANDROID_CONTENT)
│ └── 用户通过 setContentView 添加的布局
└── WindowManager.LayoutParams (窗口属性)
└── 由 WindowManagerGlobal 通过 ViewRootImpl 添加到 WMS
5. setContentView 的完整流程
结合源码看,一个典型的流程如下:
-
Activity.setContentView(layoutResID)调用
getWindow().setContentView(layoutResID),这里的getWindow()返回的就是PhoneWindow。 -
PhoneWindow.setContentView()内部调用
installDecor()创建DecorView(如果尚未创建)。 -
PhoneWindow.installDecor()-
调用
generateDecor()创建DecorView。 -
调用
generateLayout()根据 features 加载布局模板,并找到content父容器。
-
-
PhoneWindow.generateLayout()-
根据窗口特性选择对应的布局资源。
-
通过
findViewById(com.android.internal.R.id.content)获取mContentParent。 -
设置窗口背景、状态栏颜色等属性。
-
-
mContentParent.addView(layoutResID)将用户的布局 inflate 后添加到
content容器中。 -
当
Activity可见时,WindowManagerGlobal将DecorView添加到ViewRootImpl,开始测量、布局、绘制。
6. 与 ViewRootImpl 的关系
虽然 PhoneWindow 和 DecorView 负责构建窗口的视图结构,但它们并不直接和系统窗口服务通信。真正与 WindowManagerService 通信的是 ViewRootImpl。
-
ViewRootImpl持有一个DecorView引用。 -
它通过
WindowManagerGlobal.addView()将DecorView注册到系统。 -
它负责执行
performMeasure、performLayout、performDraw三大流程。 -
它也负责接收系统事件(输入、焦点、窗口状态变化)并分发给
DecorView。
所以完整链路是:
Activity → PhoneWindow → DecorView → ViewRootImpl → WindowManagerService
7. 总结
| 类 | 作用 |
|---|---|
Window |
抽象窗口定义,提供窗口操作 API |
PhoneWindow |
窗口的唯一实现,管理窗口属性、创建 DecorView、加载布局模板、分配 content 区域 |
DecorView |
窗口根视图,承载状态栏背景、导航栏背景、标题栏和用户内容,处理 insets 和事件 |
ViewRootImpl |
连接 DecorView 和系统窗口服务,执行 View 三大流程和事件分发 |
简单一句话总结:
PhoneWindow负责"窗口的逻辑和内容容器",DecorView负责"窗口的根视图和实际显示结构",两者配合实现了 Android 窗口的构建与展示。