文章目录
一. 关系图概览

- 窗口从创建到显示全流程
Activity → PhoneWindow → DecorView → ViewRootImpl → WMS → SurfaceFlinger → 屏幕。
二. 各层级概述
1. Activity
- Activity并不负责视图控制,它只是控制生命周期和处理事件。真正控制视图的是Window。
- 一个Activity包含了一个Window,Window才是真正代表一个窗口。
- Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与Window、以及View进行交互。
2. Window
- Window是视图的承载器,内部持有一个 DecorView,而这个DecorView才是 view 的根布局。
- Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。
- PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局R.layout.activity_main。
- Window 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。
- Window类型
(1)应用窗口(Application Window)
通常由 Activity 创建的窗口,每个 Activity 默认对应一个应用窗口。
(2)子窗口(Sub-window)
依附于某个应用窗口的窗口,例如:弹出的 Dialog、PopupWindow
(3)系统窗口(System Window)
由系统创建的窗口,具有较高的权限,可以悬浮在所有应用之上,例如状态栏、导航栏、来电界面、系统级悬浮窗等
(4)输入法窗口(Input Method Window)
用于显示软键盘
(5)壁纸窗口(Wallpaper Window)
类型值为 LayoutParams.TYPE_WALLPAPER
3. PhoneWindow
- 将Decoriew设置为整个应用窗口的根View,是Window的实现类。
- 它是Android中的最基本的窗口系统,每个Activity均会创建一个PhoneWindow对象,是Activity和整个View系统交互的接口。
4. DecorView
- DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。
- DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。 具体情况和Android版本及主体有关。
5. ViewRoot
- ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量,布局,绘制)均通过ViewRoot来完成。
- ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。ViewRoot继承了Handler类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。
6. WindowManager
- WindowManager 是 Android提供的一个接口,用于管理应用窗口的显示。它允许应用程序与系统窗口管理器交互,添加、更新或移除窗口。
- 开发者通常通过 Context.getSystemService(Context.WINDOW_SERVICE) 获取其实例。
- WindowManager 是客户端(应用)用来与 WindowManagerService 通信的代理。
- 应用通过 WindowManager 提交窗口请求,最终由 WindowManagerService 统一调度和管理。
- 同一个 WindowManager 可以添加多个 View,每个 View 对应一个窗口
- 每个 WindowManager 实例绑定一个 Context(Activity/Application,Activity 的 WM 会携带 Activity Token,Application 没有,所以不能直接弹 Dialog
7. WindowManagerService
- WindowManagerService(简称 WMS)是 Android 系统的核心服务之一,运行在 System Server 进程中
- 系统服务,运行在 system_server
- 它负责管理所有窗口的布局、显示顺序、焦点控制、输入事件分发等核心功能
- 它与底层的 SurfaceFlinger 交互,将窗口内容渲染到屏幕上
三. 基于源码的深入分析
1. PhoneWindow,DecorView,setContentView源码理解
- Window
抽象基类,它提供了一系列窗口的方法,比如背景,标题等等,而它的唯一实现类则是PhoneWindow
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
...
@Nullable
public View findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
/**
* Convenience for * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
* to set the screen content from a layout resource. The resource will be * inflated, adding all top-level views to the screen. * * @param layoutResID Resource ID to be inflated.
* @see #setContentView(View, android.view.ViewGroup.LayoutParams)
*/
public abstract void setContentView(@LayoutRes int layoutResID);
...
}
- PhoneWindow
Window的唯一实现类
public class PhoneWindow extends Window implements MenuBuilder.Callback {
private final static String TAG = "PhoneWindow";
...
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
private ViewGroup mContentRoot;
...
}
在PhoneWindow里面,出现了成员变量DecorView的而这里,
- DecorView则是PhoneWindow里面的一个内部类,它是继承与FrameLayout
// 既然是FrameLayout,也就可以加载布局文件,也就是说,我们那些标题栏,内容栏,
/ 顶级上看是加载在DecorView上的。而DecorView则是由PhoneWindow负责添加
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
/* package */int mDefaultOpacity = PixelFormat.OPAQUE;
/** The feature ID of the panel, or -1 if this is the application's DecorView */
private final int mFeatureId;
private final Rect mDrawingBounds = new Rect();
private final Rect mBackgroundPadding = new Rect();
private final Rect mFramePadding = new Rect();
private final Rect mFrameOffsets = new Rect();
....
}
- setContentView源码流程
activity.setContentView,实际上调用到的是它的实现类方法phoneWindow.setContentView
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// 如果没有创建 mContentParent,则初始化 DecorView 和内容父布局
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 如果不使用过渡动画,直接移除旧内容
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 使用过渡动画时,将新 View 作为过渡内容添加
final Scene newScene = Scene.getSceneForLayout(mContentParent, view, mContext);
transitionTo(newScene);
} else {
// 否则直接添加 View 到内容父布局中
mContentParent.addView(view, params);
}
}
2. Activity启动到DecorView显示的流程
前置知识:
- ViewManager
- WindowManager
- WindowManagerImpl

2.1 ViewManager
- ViewManager接口定义了一组规则,也就是add、update、remove的操作View接口。
- ViewManager是用来添加和移除activity中View的接口,可以通过Context.getSystemService()获取实例。
/** Interface to let you add and remove child views to an Activity. To get an instance
- of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
*/
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
- ViewManager实现类ViewGroup
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
private static final String TAG = "ViewGroup";
...
public void addView(View child, LayoutParams params) {
addView(child, -1, params);
}
/*
* @param child the child view to add
* @param index the position at which to add the child or -1 to add last
* @param params the layout parameters to set on the child
*/
public void addView(View child, int index, LayoutParams params) {
// addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
...
可以看到ViewGroup里面实现了ViewManager接口,View通过ViewGroup的addView方法添加到ViewGroup中,而ViewGroup层层嵌套到最顶级都会显示在在一个窗口Window中
2.2 WindowManager
- WindowManager是一个接口,而且它继承与ViewManager。WindowManager字面理解就是窗口管理器,每一个窗口管理器都与一个的窗口显示绑定
- 获取实例可以通过Context.getSystemService(Context.WINDOW_SERVICE)获取。既然继承了ViewManager,那么它也就可以进行添加删除View的操作了,不过它的操作放在它的实现类WindowManagerImpl里面。
- BadTokenException:则是addView时它的LayoutParams无效则会被抛出,或是添加第二个View的时候没有移除第一个View则会被抛出
- InvalidDisplayException:如果一个窗口是在一个二级的显示上而指定的显示找不到则会被抛出
getDefaultDisplay:返回当前WindowManager管理的显示Display - removeViewImmediate:表示从窗口上移除View,一般是当View调用了onDetachedFromWindow也就是从Window上分开后,把它移除。
- LayoutParams:静态内部类。显然是Window的布局参数,里面定义了一系列的窗口属性
/* The interface that apps use to talk to the window manager.
Use Context.getSystemService(Context.WINDOW_SERVICE) to get one of these.
*/
public interface WindowManager extends ViewManager {
public static class BadTokenException extends RuntimeException{...}
public static class InvalidDisplayException extends RuntimeException{...}
public Display getDefaultDisplay();
public void removeViewImmediate(View view);
public static class LayoutParams extends ViewGroup.LayoutParams
implements Parcelable
2.3 WindowManagerImpl
WindowManagerImpl里面有一个成员变量WindowManagerGlobal,而真正的实现则是在WindowManagerGlobal了,类似代理,只不过WindowManagerGlobal是个没有实现WindowManager的类的,自己定义了套实现。
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Display mDisplay;
private final Window mParentWindow;
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
...
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
}
- WindowManagerGlobal
public final class WindowManagerGlobal {
private static final String TAG = "WindowManager";
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
}
}
2.4 Activity启动到DecorView显示的流程
- Window,WindowManager,DecorView,WindowManagerGlobal的关系概念
- DecorView什么时候初始化?
setContentView() 是开发者在 Activity 中调用的方法,用于设置界面布局。
第一次调用该方法时,会触发 PhoneWindow 的初始化,并创建 DecorView。 - DecorView什么时候被添加
在DecorView在handleResumeActivity方法中被绑定到了WindowManager,也就是调用了windowManager.addView(decorView)

-
WindowManager 与 DecorView 的关系
Activity.setContentView()
↓
创建 PhoneWindow 和 DecorView
↓
Activity.onResume()
↓
WindowManager.addView(DecorView)
↓
创建 ViewRootImpl
↓
ViewRootImpl.setView(DecorView)
↓
与 WindowManagerService 通信,完成窗口显示 -
窗口添加流程调用关系链
WindowManager.addView()
↓
WindowManagerGlobal.addView()
↓
创建 ViewRootImpl
↓
ViewRootImpl.setView()
↓
与 WindowManagerService 通信注册窗口
本质是调用ViewRootImpl.setView
3. ViewRootImpl与View关系深入理解
3.1 基础概念理解
- ViewRootImpl,ViewRoot:ViewRoot类在Android2.2之后就被ViewRootImpl替换了。
- ViewRootImpl是一个视图层次结构的顶部,它实现了View与WindowManager之间所需要的协议,作为WindowManagerGlobal中大部分的内部实现。
- addView,removeView,update调用顺序:WindowManagerImpl -> WindowManagerGlobal -> ViewRootImp
3.2 源码理解
- ViewRootImp的setView方法
实际是一次IPC过程
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
...
try {
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
}
首先会调用到requestLayout(),表示添加Window之前先完成第一次layout布局过程,以确保在收到任何系统事件后面重新布局。requestLayout最终会调用performTraversals方法来完成View的绘制。
接着会通过WindowSession最终来完成Window的添加过程。在下面的代码中mWindowSession类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是说这其实是一次IPC过程,远程调用了Session中的addToDisPlay方法。
- requestLayout方法解读 (重点)
- checkThread 这里就是为什么子线程无法更新ui的原因所在
- scheduleTraversals最终调用的是performTraversals,经过measure、layout和draw三个过程才能将一个View绘制出来
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
3.3 重要结论
- DecorView真正的绘制在什么时候
DecorView真正的绘制显示是在activity.handleResumeActivity方法中DecorView被添加到WindowManager时候,也就是调用到windowManager.addView(decorView)。而在windowManager.addView方法中调用到windowManagerGlobal.addView,开始创建初始化ViewRootImpl,再调用到viewRootImpl.setView,最后是调用到viewRootImpl的performTraversals来进行view的绘制(measure,layout,draw),这个时候View才真正被绘制出来。
- 如何获取view的实际高度,在哪个时机
onCreate不可以:实际调用时机activity.handleResumeActivity最后调用到的是activity的onResume方法
onResume不可以:原因在于绘制是异步的
- 为什么在onCreate里面的子线程去更新UI的不报错(如果放置个button再去点击就报错)
ViewRootImpl的初始化有关,因为在onCreate的时候此时View还没被绘制出来,ViewRootImpl还未创建出来,它的创建是在activity.handleResumeActivity的调用到windowManager.addView(decorView)时候,如前面说的ViewRootImpl才被创建起来
- 每一个View对应一个ViewRoot
四. Dialog,PopupWindow,Toast窗口机制
仅介绍部分知识,更加详细的见 添加链接描述
1. WindowManager.LayoutParams
有三种窗口类型Type
- 应用程序窗口 : type值在 FIRST_APPLICATION_WINDOW ~ LAST_APPLICATION_WINDOW
须将token设置成Activity的token。
eg: 前面介绍的Activity窗口,Dialog - 子窗口: type值在 FIRST_SUB_WINDOW ~ LAST_SUB_WINDOW SubWindows与顶层窗口相关联,需将token设置成它所附着宿主窗口的token。
eg: PopupWindow(想要依附在Activity上需要将token设置成Activity的token) - 系统窗口: type值在 FIRST_SYSTEM_WINDOW ~ LAST_SYSTEM_WINDOW SystemWindows不能用于应用程序,使用时需要有特殊权限,它是特定的系统功能才能使用。
eg: Toast,输入法等。
public static class LayoutParams extends ViewGroup.LayoutParams
implements Parcelable {
//窗口的绝对XY位置,需要考虑gravity属性
public int x;
public int y;
//在横纵方向上为相关的View预留多少扩展像素,如果是0则此view不能被拉伸,其他情况下扩展像素被widget均分
public float horizontalWeight;
public float verticalWeight;
//窗口类型
//有3种主要类型如下:
//ApplicationWindows取值在FIRST_APPLICATION_WINDOW与LAST_APPLICATION_WINDOW之间,是常用的顶层应用程序窗口,须将token设置成Activity的token;
//SubWindows取值在FIRST_SUB_WINDOW和LAST_SUB_WINDOW之间,与顶层窗口相关联,需将token设置成它所附着宿主窗口的token;
//SystemWindows取值在FIRST_SYSTEM_WINDOW和LAST_SYSTEM_WINDOW之间,不能用于应用程序,使用时需要有特殊权限,它是特定的系统功能才能使用;
public int type;
//WindowType:开始应用程序窗口
public static final int FIRST_APPLICATION_WINDOW = 1;
//WindowType:所有程序窗口的base窗口,其他应用程序窗口都显示在它上面
public static final int TYPE_BASE_APPLICATION = 1;
//WindowType:普通应用程序窗口,token必须设置为Activity的token来指定窗口属于谁
public static final int TYPE_APPLICATION = 2;
//WindowType:应用程序启动时所显示的窗口,应用自己不要使用这种类型,它被系统用来显示一些信息,直到应用程序可以开启自己的窗口为止
public static final int TYPE_APPLICATION_STARTING = 3;
//WindowType:结束应用程序窗口
public static final int LAST_APPLICATION_WINDOW = 99;
//WindowType:SubWindows子窗口,子窗口的Z序和坐标空间都依赖于他们的宿主窗口
public static final int FIRST_SUB_WINDOW = 1000;
//WindowType: 面板窗口,显示于宿主窗口的上层
......
public final int copyFrom(LayoutParams o) {
......
}
......
public void scale(float scale) {
......
}
......
}
2. Token
- 基础概念
- token是用来表示窗口的一个令牌,只有符合条件的token才能被WMS通过添加到应用上。
- 首先对于Activity里面的token,它的创建则是在AMS启动Activity开始的,之后保存在ActivityRecord.appToken中。而对于Activity中的token绑定到对应的Window上(具体是在activity.attach方法中创建的)

- token分类
AppToken:对应一个 Activity,生命周期与 Activity 绑定。
WindowToken:对应一组窗口,用于校验窗口合法性。
Activity 窗口用 AppToken,普通子窗口用 WindowToken。
五. WMS初探
1. 核心原理
- 窗口的添加与移除
添加窗口:当Activity启动时,WMS会:分配一个Surface(绘制的画布),计算窗口的位置和大小,决定它的Z轴顺序(谁盖住谁?)
移除窗口:当Activity关闭时,WMS会:释放对应的Surface,调整其他窗口的布局(比如输入法退出后,界面要重新调整) - 窗口的层级(Z-order)
WMS用Z-order决定窗口的显示顺序,比如:系统窗口(如状态栏) 通常在最顶层(Z轴值最大),普通Activity窗口 在中间层,壁纸(Wallpaper) 在最底层 - 触摸事件的分发
当你点击屏幕:InputManager 收到触摸事件,WMS 根据窗口的Z-order,找到最顶层的窗口
WMS 把事件交给对应的窗口处理(比如Activity或Dialog) - 窗口动画
Activity切换、Dialog弹出时的动画,也是由WMS控制的:
WMS 告诉SurfaceFlinger(负责合成的系统服务)如何做动画
比如:平移、缩放、淡入淡出
2. App 与 WMS 通信方式
通信通道:IWindowSession(每个进程一个)
窗口回调:IWindow(每个窗口一个)
Binder 作用:跨进程传递 窗口操作、尺寸、事件、生命周期。
App (Client)
└──> WindowManager.addView()
└──> WindowManagerGlobal.addView()
└──> IWindowManager.addWindow() (Binder 调用)
└──> WMS.addWindow() (系统端处理)
3. WMS 感知窗口生命周期
- 与 AMS 的交互
AMS 负责管理 Activity 的生命周期。
当 Activity 进入前台、后台或被销毁时,AMS 会通知 WMS。
WMS 根据这些信息更新窗口的可见性、焦点状态、动画等。 - 与 ViewRootImpl 的交互
每个窗口都有一个 ViewRootImpl 对象,它负责与 WMS 进行 Binder 通信。
ViewRootImpl 调用 IWindowSession 接口与 WMS 通信,执行窗口添加、布局、移除等操作。
WMS 通过这些调用感知窗口的创建(addWindow)、布局变化(relayoutWindow)、移除(removeWindow)等生命周期事件。 - 窗口状态变更回调
WMS 会通过 WindowState 跟踪窗口的状态(如是否可见、是否获得焦点等)。
当窗口状态发生变化时,WMS 会更新其内部状态,并可能触发动画、输入法切换等行为。
六. Surface 与窗口渲染
1. Surface 的基本概念
Surface 是 Android 中用于绘制图形的缓冲区(BufferQueue),它本质上是一个生产者-消费者模型。
应用作为生产者向 Surface 提交图形数据(如通过 Canvas、OpenGL、Vulkan 等),而 SurfaceFlinger 作为消费者负责将这些图形数据合成并显示到屏幕上。
2. Surface 在窗口机制中的作用
- 作为窗口的绘图“画布”
每个窗口(如 Activity、Dialog、Toast、壁纸等)都有一个对应的 Surface。
应用通过 ViewRootImpl 获取到该窗口的 Surface,并在其上进行绘制。 - 与 WMS 协作完成窗口管理
当 App 调用 WindowManager.addView() 时,WMS 会为该窗口创建一个 Surface。
WMS 会将窗口的布局信息(如大小、位置)传递给 App,App 通过 relayout() 获取 Surface 和布局信息。 - 参与窗口层级(Z-Order)和透明度管理
Surface 会携带窗口的层级(layer)、透明度(alpha)、变换矩阵(transform)等属性。
这些属性由 WMS 计算后传递给 SurfaceFlinger,用于最终的合成。 - 支持双缓冲与硬件加速
Surface 使用 BufferQueue 实现双缓冲机制,提升绘制效率。
支持 GPU 渲染,实现硬件加速。
3. 绘制流程
- App 请求添加窗口
调用 WindowManager.addView()。 - WMS 创建窗口状态
调用 addWindow(),创建 WindowState。 - App 请求 Surface
调用 relayoutWindow(),请求布局并获取 Surface。 - WMS 创建 Surface 并返回
调用 SurfaceControl 创建 Surface,并通过 Binder 返回给 App。 - App 使用 Surface 进行绘制
通过 Canvas 或 OpenGL 绘制内容到 Surface。 - SurfaceFlinger 合成所有窗口
所有窗口的 Surface 被提交给 SurfaceFlinger,进行最终合成显示。 - 完整流程
App 绘制 → 写入 Surface → 提交给 SurfaceFlinger → SurfaceFlinger 合成 → 屏幕显示。
4. SurfaceView、TextureView 与普通 View 区别
普通 View:共享同一个 Surface,在主线程绘制。
SurfaceView:独立 Surface,可子线程绘制,效率高,但不支持动画、平移旋转。
TextureView:有独立缓冲,但挂载在应用窗口中,支持变换、动画。
5. invalidate/requestLayout 如何到 WMS & SF
invalidate/requestLayout → ViewRootImpl
scheduleTraversals
主线程 performTraversals
measure/layout/draw
绘制内容写入 Surface
setVisible/relayout 通知 WMS
WMS 通知 SF 合成刷新
七. 多窗口、分屏、折叠屏 , 申请悬浮窗 todo
八. 思维导图


3033

被折叠的 条评论
为什么被折叠?



