包含所有其他View,分配每个View的位置和大小,其中View的大小允许View参与进来设置,但是View的位置只能由DecorView决定。 Activity中DecorView的加载流程: Activity管理着window,window管理着DecorView,因此Activity间接管理着DecorView。Activity处在”create“状态的时候创建对应的Window,并在setContentView里创建 DecorView,最终添加自定义布局到DecorView里。此时,Activity创建完毕,Window创建完毕,DecorView创建完毕,ViewTree构建成功,三者关系已建立。Acitivity处在“resume”状态的时候,通过Window的管理类将DecorView添加到Window里,并提交更新DecorView的请求。当下次屏幕刷新信号到来之时,执行ViewTree三大流程(测量,布局,绘制),最终的效果就是我们的自定义布局显示在屏幕上。结合源码对上述内容进行解释分析
时间: 2025-07-22 08:30:45 浏览: 13
<think>我们正在讨论Android中DecorView的工作机制及其在Activity生命周期中的加载流程。用户要求结合源码进行解释,特别是DecorView如何管理View的位置和大小以及如何参与ViewTree的三大流程(测量、布局、绘制)。
根据提供的引用,我们可以梳理出以下关键点:
1. DecorView是ViewTree的根视图(引用[1])。
2. PhoneWindow持有DecorView(引用[1])。
3. ViewRootImpl是连接WindowManager和DecorView的纽带,负责三大流程(引用[2])。
4. 将DecorView添加到Window时,会创建ViewRootImpl并建立关联(引用[2])。
5. WindowManager.addView()将DecorView和WindowManager关联,并最终触发三大流程(引用[3])。
6. Activity不负责视图控制,而是通过Window(引用[4])。
7. 布局的加载是通过递归解析XML文件完成的(引用[5])。
现在,我们将结合源码详细分析DecorView的创建、加载以及参与三大流程的过程。
### 1. DecorView的创建
在Activity的创建过程中,会调用`setContentView()`方法,该方法最终会调用PhoneWindow的`setContentView()`方法。在PhoneWindow中,会创建DecorView。
**源码分析(基于Android SDK 30):**
```java
// PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor(); // 初始化DecorView
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent); // 将Activity的布局添加到mContentParent
}
```
在`installDecor()`方法中:
```java
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(); // 创建DecorView
...
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); // 生成布局,并设置ContentView的父容器
...
}
}
```
`generateDecor()`方法:
```java
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
```
### 2. 将布局加载到DecorView
在`generateLayout()`方法中,会根据主题等设置选择不同的窗口布局,然后通过`mDecor.onResourcesLoaded()`加载布局。
```java
protected ViewGroup generateLayout(DecorView decor) {
// 根据主题选择布局文件
int layoutResource = R.layout.screen_title; // 例如
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); // 加载布局
ViewGroup contentParent = (ViewGroup) findViewById(ID_ANDROID_CONTENT); // 获取content区域
...
return contentParent;
}
```
在`DecorView`的`onResourcesLoaded()`方法中,会将布局文件加载到DecorView中,并设置到mContentRoot(即整个DecorView的根布局)中。
### 3. 将Activity的布局添加到content区域
在`setContentView()`方法中,通过`mLayoutInflater.inflate(layoutResID, mContentParent)`将Activity的布局文件添加到`mContentParent`(即content区域)中。这样,Activity的布局就成为了DecorView中content区域的子视图。
### 4. ViewRootImpl的创建与关联
在Activity的`onResume()`方法之后,ActivityThread会调用`handleResumeActivity()`方法,其中会调用Activity的`makeVisible()`方法,将DecorView添加到WindowManager中。
**源码分析(ActivityThread.handleResumeActivity()):**
```java
@Override
public void handleResumeActivity(...) {
...
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
...
wm.addView(decor, l); // 将DecorView添加到WindowManager
}
}
```
`WindowManager`的实现是`WindowManagerImpl`,其`addView()`方法会调用到`WindowManagerGlobal`的`addView()`方法:
```java
// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params, ...) {
...
ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView); // 将DecorView设置给ViewRootImpl
}
```
在`ViewRootImpl`的`setView()`方法中,会调用`requestLayout()`来开始视图的三大流程。
### 5. 三大流程的触发
在`ViewRootImpl.setView()`中:
```java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout(); // 请求布局
...
}
```
`requestLayout()`方法:
```java
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals(); // 安排遍历,即开始三大流程
}
}
```
`scheduleTraversals()`方法会向主线程消息队列发送一个消息,最终在下一个VSync信号到来时执行`performTraversals()`方法。`performTraversals()`方法包含了测量(measure)、布局(layout)和绘制(draw)三大流程。
### 6. DecorView如何管理View的位置和大小
在`performTraversals()`方法中:
```java
private void performTraversals() {
...
// 测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
// 布局
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
// 绘制
performDraw();
}
```
- **测量(measure)**:从DecorView开始,递归调用每个View的`measure()`方法,确定每个View的尺寸。
- **布局(layout)**:从DecorView开始,递归调用每个View的`layout()`方法,确定每个View的位置。
- **绘制(draw)**:从DecorView开始,递归调用每个View的`draw()`方法,将内容绘制到屏幕上。
DecorView作为根视图,是整个ViewTree的起点。在测量过程中,DecorView会根据窗口的尺寸和自身的LayoutParams计算出自己的测量规格,然后传递给子视图(即content区域)。同样,在布局过程中,DecorView会确定content区域的位置和大小,然后content区域再负责布局其子视图。
### 总结流程
1. **创建DecorView**:在Activity的`setContentView()`中,PhoneWindow创建DecorView。
2. **加载窗口布局**:根据主题选择预定义的窗口布局,加载到DecorView中,并找到content区域(id为android.R.id.content)。
3. **加载Activity布局**:将Activity的布局文件加载到content区域中。
4. **关联WindowManager**:在Activity的`onResume()`之后,将DecorView添加到WindowManager,同时创建ViewRootImpl并建立关联。
5. **触发三大流程**:ViewRootImpl的`requestLayout()`会安排一次遍历,最终执行`performTraversals()`,开始测量、布局和绘制。
这样,DecorView作为整个视图树的根,通过ViewRootImpl与WindowManager交互,并负责管理其子视图的位置和大小,以及参与整个ViewTree的三大流程。
[^1]: 创建DecorView,作为整个ViewTree的RootView
[^2]: ViewRootImpl是连接WindowManager和DecorView的纽带
[^3]: WindowManager.addView()将DecorView添加到窗口,并最终触发三大流程
[^4]: Activity通过Window控制视图
[^5]: 布局加载通过递归解析XML完成
§§相关问题§§
1. DecorView和ViewRootImpl之间的关系是什么?
2. 在Activity的生命周期中,DecorView是在哪个阶段被添加到窗口的?
3. View的三大流程(测量、布局、绘制)是如何从DecorView开始的?
4. 如何自定义DecorView?有什么注意事项?
5. 为什么说DecorView是ViewTree的根视图?</think>### Android Activity生命周期中DecorView的创建与加载流程详解
#### 1. **DecorView的创建与初始化**
在Activity启动时,系统通过`PhoneWindow`创建`DecorView`作为整个视图树的根容器:
```java
// PhoneWindow.java
public void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(); // 创建DecorView实例
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor); // 关联布局
}
}
```
- **关键步骤**:
- `generateDecor()` 实例化`DecorView`(继承`FrameLayout`)[^1]
- `generateLayout()` 根据主题加载预定义布局(如`R.layout.screen_simple`),将`android.R.id.content`作为Activity布局的父容器[^1][^4]
#### 2. **布局加载与挂载**
通过`LayoutInflater`递归解析XML布局:
```java
// LayoutInflater.java
void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs) {
while (parser.next() != XmlPullParser.END_TAG) {
final View view = createViewFromTag(parent, name, context, attrs); // 创建View
final ViewGroup viewGroup = (ViewGroup) parent;
viewGroup.addView(view); // 添加到父容器
rInflateChildren(parser, view, attrs); // 递归加载子View[^5]
}
}
```
- **流程说明**:
- Activity的`setContentView(R.layout.activity_main)`触发布局加载
- 解析后的View树挂载到`DecorView`的`content`区域[^1]
- 最终形成完整的ViewTree:`DecorView → 系统布局(如ActionBar) → content → Activity布局`
#### 3. **关联Window与ViewRootImpl**
在`onResume()`阶段完成关键绑定:
```java
// ActivityThread.java
public void handleResumeActivity(...) {
ViewManager wm = activity.getWindowManager();
wm.addView(decorView, params); // 关联WindowManager[^3]
}
```
- **核心对象**:
- `ViewRootImpl` 作为`WindowManager`和`DecorView`的纽带[^2]
- `WindowManager.addView()` 触发`ViewRootImpl`创建,建立`DecorView`与`WindowManager`的关联[^3]
#### 4. **三大流程的触发与执行**
`ViewRootImpl`驱动整个ViewTree的测量、布局、绘制:
```java
// ViewRootImpl.java
public void performTraversals() {
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 1.测量
performLayout(lp, desiredWindowWidth, desiredWindowHeight); // 2.布局
performDraw(); // 3.绘制
}
```
- **测量流程**:
- 从`DecorView`开始递归调用`measure()`
- 计算规则:`DecorView`尺寸 = 屏幕尺寸 - 状态栏/导航栏
- **布局流程**:
- `DecorView.layout(0, 0, width, height)` 确定自身位置
- 递归调用子View的`layout()`传递位置参数
- **绘制流程**:
- `DecorView.draw(canvas)` 先绘制自身背景
- 递归调用子View的`draw()`实现视图渲染
#### 5. **位置与尺寸管理机制
- **坐标系转换**:
- `DecorView`的坐标系原点`(0,0)`位于屏幕左上角(不包括状态栏)
- 子View通过`getLocationOnScreen()`获取绝对坐标
- **尺寸约束**:
- `DecorView`接受`WindowManager.LayoutParams`的尺寸规则
- 通过`ViewRootImpl`向WMS(WindowManagerService)申请窗口尺寸[^3]
#### 6. **与Activity生命周期的联动
| 生命周期阶段 | DecorView相关操作 |
|--------------|------------------------------------------|
| onCreate() | 创建PhoneWindow和DecorView,加载布局 |
| onStart() | DecorView完成attach但不可见 |
| onResume() | ViewRootImpl关联DecorView,触发首次绘制 |
| onPause() | 暂停绘制,但DecorView仍存在 |
| onDestroy() | WindowManager.removeView()销毁DecorView |
### 关键源码总结
1. **创建根源**:`PhoneWindow.generateDecor()`
2. **布局挂载**:`mContentParent.addView(activity_main)`
3. **流程驱动**:`ViewRootImpl.performTraversals()`
4. **尺寸协商**:`ViewRootImpl.relayoutWindow()`与WMS通信[^3]
DecorView作为Android视图系统的根容器,通过`ViewRootImpl`协调`WindowManager`和`ViewTree`,最终实现测量→布局→绘制的完整渲染流水线[^2][^3]。
---
阅读全文