Window层级结构
相关类简介
这里只介绍与UI层级相关的功能。
Window:抽象类,定义了window视图布局的一些规则,如是否显示ActionBar之类。
PhoneWindow:Window的唯一实现类,与Activity一一对应,持有了具体的View对象,对外提供操作这些View的具体实现。
DecorView:顶级View的具体实现,继承于FrameLayout,被PhoneWindow所持有。包含顶部状态栏与ContentView。
ContentView:这个不是一个具体存在的类,而是Framework中包含了很多已经实现的Content布局,根据用户的配置来选择加载合适的放置于DecorView中,例如有无ActionBar对应了两种不同的布局资源。(这里需要注意Activity.setContentView()设置的View是ContentView的子View而不是ContentView自己)
WindowUI层级

Window层级相关源码
Window:主要提供与DesorView相关的配置参数,如状态栏、ActionBar之类,具体源码不再赘述。
PhoneWindow:
1 | // This is the top-level view of the window, containing the window decor. |
DecorView
1 | // 对应上图的ContentView |
上述几个View的初始化流程:
1 | // 以Activity.setContentView()为起点 |
setContentView流程图
上面的代码展示了setContentView()为起点的各个层级View的初始化过程,用图表示如下:

一些总结
从源码分析可以得到以下结论:
- View的根视图是DecorView
- 一个Activity系统已有的视图层次是三层,DecorView->ContentView->content(开发者设置的View的容器)
- 设置窗口属性需要在Activity.setContentView()之前
- 最重要的是几个类在UI布局上的分工,Window提供DecorView配置的参数,PhoneWindow提供加载DecorView的具体实现,DecorView则控制状态栏等布局的具体实现。
WindowManager与Activity
这一节主要介绍WindowManager与Activity之间的关系,以及核心实例的创建过程。
相关类以及它们之间的联系
PhoneWindow:上面介绍过了,这里不再赘述。
WindowManager:The interface that apps use to talk to the window manager.
WindowManagerImpl:WindowManager和ViewManager的实现类,通过WindowManagerGlobal与WMS通信。
WindowManagerGlobal:单例,代理了WindowManagerImpl的功能,与WMS通信。全局管理所有的Window,相当于一个消息总线。
ViewRootImpl:与顶级View一一对应,绘制、触摸等事件由Window传递到View的中间节点。持有WindowSession通过Binder与WMS通信,同时持有IWindow作为WMS的回调接口,用于例如touch事件的回调。
用一张图表示他们之间的关系:

从源码分析相关实例创建流程
Activity与Window相关类是绑定在一起的,因此Window相关类的实例化都是融合在Activity的生命周期中的。
1、启动WMS
1 | // ActivityThread.java |
2、Activity的实例化
1 | // ActivityThread.java |
3、Window相关类的创建以及它们与Activity的绑定
1 | final void attach(Context context, ActivityThread aThread, |
4、WindowManager的实例化
1 | /** |
1 | // Window.java |
从上面的代码可以看出,Activity与Window持有的是同一个WindowManager实例,但是这个实例不同于SystemServiceRegistry中注册的那个WindowManager实例,它们是父子关系
5、RootViewImpl的实例化
前面的代码实例化了Activity以及相关的Window类,接下来是处理View相关的逻辑。
先继续上面提到的ActivityThread.handleResumeActivity()方法
1 | // ActivityThread.handleResumeActivity() |
1 | // WindowManagerImpl.addView() |
最后ViewRootImpl被实例化并和DecorView一起被全局保存在WindowManagerGlobal中
1 | // WindowManagerGlobal.java |
用一张图表示创建流程
补充:WindowManagerGlobal.addView()会调用到ViewRootImpl.setView(),并进一步调用到ViewRoot
Impl.requestLayout(),进入View的绘制流程,完成后设置DecorView可见。

ViewRootImpl与View
上一节介绍了WindowManager与Activity的关系,并且提到了ViewRottImpl的实例化,这一节会更深入的了解ViewRootImpl,并以此介绍WindowManager与View绘制、以及事件分发处理的一些细节。
ViewRootImpl简介
The top of a view hierarchy, implementing the needed protocol between View and the WindowManager. This is for the most part an internal implementation detail of WindowManagerGlobal.
作为View与绘制系统的桥梁,ViewRootImpl包含了View测量、布局、绘制的起点函数,同时,它也作为触摸事件传递的中介,将触摸信号由native传递到framework。
ViewRootImpl与View的绘制流程
View的测量
1
2
3
4
5
6
7
8
9// ViewRootImpl.requestLayout()
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// 在这里检查当前是否在主线程,防止在非主线程修改UI
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}以requestLayout()为入口,调用scheduleTraversals(),然后调用doTraversal()->performTraversals(),最终通过performMeasure()调用到DecorView的measure()方法。
1
2
3
4
5
6
7
8
9
10private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}View的布局
主要的流程与上述过程类似,以requestLayout()为入口,调用scheduleTraversals(),然后调用doTraversal()->performTraversals(),最终通过performLayout()调用到DecorView的layout()方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
if (host == null) {
return;
}
try {
// 开始layout流程
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
// 这里的逻辑是处理在performLayout时,如果用户主动调用了requestLayout(),那么这个layout行为不会立即生效,而是记录在mLayoutRequesters中,在host.layout()结束后再逐一调用。关于requestLayout()的细节后文再作介绍。
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
for (int i = 0; i < numValidRequests; ++i) {
final View view = validLayoutRequesters.get(i);
view.requestLayout();
}
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}View的绘制
主要的流程与上述过程类似,以requestLayout()为入口,调用scheduleTraversals(),然后调用doTraversal()->performTraversals(),最终通过performDraw()调用到DecorView的draw()方法。1
2
3
4
5
6
7
8
9
10
11
12private void performDraw() {
// 标记是否需要重新绘制整个屏幕
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
try {
// 这个方法会调用到DecorView
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
}
}View.requestLayout()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// View.requestLayout
public void requestLayout() {
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
ViewRootImpl viewRoot = getViewRootImpl();
// 如上文所说的,如果正在layout步骤,则将该请求加入等待队列
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
}
// 一直往上调用mParent的requestLayout()
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}由上面的代码可以知道,最终这个请求会调用到DecorView的requestLayout(),而DecorView的mParent就是RootViewImpl,因此layout的请求就会交由RootViewImpl来执行。
View.invalidate()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34// View.java
public void invalidate() {
invalidate(true);
}
void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
//这里判断该子View是否可见或者是否处于动画中
if (skipInvalidate()) {
return;
}
//根据View的标记位来判断该子View是否需要重绘,假如View没有任何变化,那么就不需要重绘
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
// ...
//把需要重绘的区域传递给父容器
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
//调用父容器的方法,向上传递事件
p.invalidateChild(this, damage);
}
// ...
}
}与requestLayout的逻辑类似,invalidate过程会向上调用invalidateChild(),而invalidateChild()则会调用当前View的invalidateChildInParent(),这个方法的作用是计算需要绘制的子View在当前View中的位置。最后这两个方法都会调用到ViewRootImpl的同名方法中。
在ViewRootImpl的invalidateChildInParent()中也会调用checkThread()进行线程判断,防止在非UI线程发起绘制请求。
ViewRootImpl的invalidateChildInParent()最终依然会调用到scheduleTraversals方法,触发View的工作流程。不过由于没有添加measure和layout的标记位,因此measure、layout流程不会执行,而是直接从draw流程开始。
总结一下就是每一层都需要做两件事,一个是计算需要绘制的View在当前层的坐标,一个是将绘制请求发往上一层View。
ViewRootImpl.setView()
上面的绘制流程是用户主动触发的一个流程,而在Activity的创建过程中,系统会触发一次整个流程,入口就是ViewRootImpl.setView()。这个方法会调用requestLayout()进入绘制流程。
ViewRootImpl与MotionEvent

上述流程需要注意的几个点:
在ViewRootImpl中,有一系列类似于InputStage(输入事件舞台)的概念,每种InputStage可以处理一定的事件类型,比如AsyncInputStage、ViewPreImeInputStage、ViewPostImeInputStage等。当一个InputEvent到来时,ViewRootImpl会寻找合适它的InputStage来处理。对于点击事件来说,ViewPostImeInputStage可以处理它。
点击事件从DecorView传递到Activity的时候,注意到DecorView.dispatchTouchEvent()并没有调用super方法,因此避免了在此时将点击事件传入View树。这里的Callback就是Activity,绑定发生在attach()的时候。
1
2
3
4
5
6// DecorView.dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}最后进入View树是在DecorView.superDispatchTouchEvent()中调用了父类,即View的dispatchTouchEvent方法。
一些总结
- 布局绘制的请求发起必须要在主线程。
- 不能在主线程中发起布局绘制请求的原因是requestLayout()和invalidateChildInParent()会checkThread。
- 在onCreate()与onResume()过程中无法取得View的宽高值是因为绘制流程的开始是在onResume()之后。
- 由于添加window的操作在requestlayout()之后,因此可以在onAttachedToWindow()时获取控件大小。
Window与WindowManagerService
上面介绍的是Window与Activity或者与View之间的关系,并没有介绍Window自己,下面的内容则是介绍Window自己的作用以及如何被WMS管理等。
Window
一个window恰如你在计算机中看到的一个window。它拥有唯一一个用以绘制自己的内容的surface。应用通过 Window Manager创建一个window,Window Manager 为每一个window创建一个surface,并把该surface传递给应用以便应用在上面绘制。应用可以在surface上任意进行绘制。对于Window Manager来说,surface就是一个不透明的矩形而已。
WindowManagerService作用
- 为所有窗口分配Surface。客户端向WMS添加一个窗口的过程,其实就是WMS为其分配一块Suiface的过程,一块块Surface在WMS的管理下有序的排布在屏幕上。Window的本质就是Surface。
- 管理Surface的显示顺序、尺寸、位置
- 管理窗口动画
- 输入系统相关:WMS是派发系统按键和触摸消息的最佳人选,当接收到一个触摸事件,它需要寻找一个最合适的窗口来处理消息,而WMS是窗口的管理者,系统中所有的窗口状态和信息都在其掌握之中,完成这一工作不在话下。
Window种类
- 应用Window:与Activity一一对应,层级范围是1-99
- 子Window:不能单独存在,需要依附在指定的父Window上,层级范围是1000-1999
- 系统Window:需要声明权限才能创建,例如Toast就是系统Window,层级范围是2000-2999
Window与WMS的绑定
通过ViewRootImpl完成双向的通信
1 | // ViewRootImpl.java |
Window的添加

删除的过程比较类似,就不列出来了。
添加自己的Window
1 | WindowManager wm = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE); |
参考资料
android activity setContentView()方法执行流程分析
Android View 深度分析requestLayout、invalidate与postInvalidate
Android中MotionEvent的来源和ViewRootImpl