ViewRootImpl 是 Android 视图系统中一个极其核心的内部类,它不是一个普通的 View,而是连接窗口管理器(WindowManager)和 View 树的"枢纽" 。你可以把它理解为一个窗口的"总管家",负责驱动整个 View 树的测量、布局、绘制 ,以及接收和分发所有的输入事件。
1. 入口与创建:从 WindowManager.addView 开始
每个需要显示在屏幕上的窗口(如 Activity、Dialog、Toast),最终都会通过 WindowManager.addView() 将 DecorView(视图树的根)添加到系统中。
-
WindowManagerGlobal会为这个窗口创建一个ViewRootImpl实例。 -
随后调用其
setView()方法,这一步至关重要:- 保存 View 根 :将传入的
DecorView赋值给ViewRootImpl的mView成员变量,作为后续操作的对象。 - 向系统添加窗口 :通过
IWindowSession这个跨进程通信(AIDL)接口,向系统服务WindowManagerService(WMS) 请求添加一个窗口。 - 初始化输入通道 :建立一个
InputChannel,为后续接收来自系统InputFlinger的输入事件做准备。
- 保存 View 根 :将传入的
scss
//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
//当前窗口为子窗口,需要让父窗口对当前窗口布局参数进行一些修改
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
...
//同一个view不允许添加两次
int index = findViewLocked(view, false);
if (index >= 0) {
...
}
...
//创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//保存到全局变量中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//将View设置给ViewRootImpl,将触发真正的窗口添加过程
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
synchronized (this) {
if (mView == null) {
mView = view;
...
//注册监听,监听屏幕变化(如横竖屏、折叠屏状态)
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
//保存视图布局方向
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
//处理未捕获的按键事件
mFallbackEventHandler.setView(view);
//复制 Window 属性
mWindowAttributes.copyFrom(attrs);
...
//强制设置`PRIVATE_FLAG_USE_BLAST`(BLAST是Android 10+引入的新Surface合成架构)
mWindowAttributes.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
attrs = mWindowAttributes;
...
if (view instanceof RootViewSurfaceTaker) {
//允许应用自己管理Surface,而不是由WindowManager绘制
...
}
...
if (mSurfaceHolder == null) {
//启用硬件加速
//只有非自绘Surface的窗口才会尝试开启硬件加速(GPU渲染)
enableHardwareAcceleration(attrs);
...
}
...
//请求首次布局
requestLayout();
//创建InputChannel,用于接收输入事件(触摸/按键),此时还是空壳
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
...
try {
...
// 通过 Binder请求WMS添加窗口,携带参数inputChannel
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
mTempControls);
...
}
...
//处理添加窗口返回结果
if (res < WindowManagerGlobal.ADD_OKAY) {
//表示添加失败,根据不同错误码抛出对应的运行时异常
//常见的失败原因:Activity已退出、Token无效、权限不足、窗口类型重复等
...
//创建`WindowInputEventReceiver`,接收原始输入事件,
//启动应用内的分发流程
if (inputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
...
}
//ViewRootImpl将作为view的Parent
view.assignParent(this);
...
}
}
}
//添加窗口流程
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
//清空输出参数:防止返回脏数据
Arrays.fill(outActiveControls, null);
int[] appOp = new int[1];
final boolean isRoundedCornerOverlay = (attrs.privateFlags
& PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
//检查调用者是否有权限添加指定类型的窗口。
//不同类型的窗口需要不同权限,如 `TYPE_SYSTEM_ALERT`
//需要 `SYSTEM_ALERT_WINDOW` 权限。
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
//如果权限不足,直接返回错误码。
if (res != ADD_OKAY) {
return res;
}
WindowState parentWindow = null;
//获取调用者的PID和UID
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
//临时清除调用者的 Binder 身份,让后续操作以系统身份执行(用于权限绕过,
//之后 `restoreCallingIdentity` 恢复)
final long origId = Binder.clearCallingIdentity();
final int type = attrs.type;
synchronized (mGlobalLock) {
if (!mDisplayReady) {
throw new IllegalStateException("Display has not been initialialized");
}
//根据 `displayId` 获取对应的显示器内容对象。
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
//显示器不存在。
if (displayContent == null) {
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//应用无权访问
if (!displayContent.hasAccess(session.mUid)) {
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
//`mWindowMap` 是 WMS 维护的 `IBinder -> WindowState` 映射表
//检查窗口是否重复添加
if (mWindowMap.containsKey(client.asBinder())) {
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//如果是子窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
//子窗口必须依附于一个父窗口,父窗口必须存在
if (parentWindow == null) {
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
//父窗口不能是子窗口(不允许嵌套子窗口)
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
//私有演示窗口:只能在私有显示器上添加
if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
return WindowManagerGlobal.ADD_PERMISSION_DENIED;
}
//演示窗口:显示器必须支持公开演示
if (type == TYPE_PRESENTATION && !displayContent.getDisplay().isPublicPresentation()) {
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
int userId = UserHandle.getUserId(session.mUid);
//如果请求的用户 ID 与调用者不同,
if (requestUserId != userId) {
try {
//校验调用者是否有权限切换到该用户。
//多用户场景下,确保窗口添加到正确的用户空间
mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId,
false /*allowAll*/, ALLOW_NON_FULL, null, null);
} catch (Exception exp) {
return WindowManagerGlobal.ADD_INVALID_USER;
}
userId = requestUserId;
}
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
//获取WindowToken
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
//子窗口复用父窗口的 Token:保证同一窗口组的 Token 一致
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
final IBinder windowContextToken = attrs.mWindowContextToken;
//Token不存在
if (token == null) {
...
if (hasParent) {
//子窗口复用父窗口的 Token
token = parentWindow.mToken;
} else if (mWindowContextListenerController.hasListener(windowContextToken)) {
final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;
...
//创建WindowToken
token = new WindowToken.Builder(this, binder, type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.setRoundedCornerOverlay(isRoundedCornerOverlay)
.setFromClientToken(true)
.setOptions(options)
.build();
} else {
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
//创建WindowToken
token = new WindowToken.Builder(this, binder, type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.setRoundedCornerOverlay(isRoundedCornerOverlay)
.build();
}
}
//Token存在
else if (rootType >= FIRST_APPLICATION_WINDOW
&& rootType <= LAST_APPLICATION_WINDOW) {
//应用窗口,token是 `ActivityRecord`(即 Activity 的 Token)
activity = token.asActivityRecord();
//ActivityRecord不存在
if (activity == null) {
return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
} else if (activity.getParent() == null) {
...
}
//输入法窗口,Token 类型必须匹配
} else if (rootType == TYPE_INPUT_METHOD) {
if (token.windowType != TYPE_INPUT_METHOD) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//语音交互窗口,Token 类型必须匹配
} else if (rootType == TYPE_VOICE_INTERACTION) {
if (token.windowType != TYPE_VOICE_INTERACTION) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//壁纸窗口,Token 类型必须匹配
} else if (rootType == TYPE_WALLPAPER) {
if (token.windowType != TYPE_WALLPAPER) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//Toast 窗口,需要 Token
} else if (type == TYPE_TOAST) {
addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
callingUid, parentWindow);
if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//快捷设置对话框,Token 类型必须匹配
} else if (type == TYPE_QS_DIALOG) {
if (token.windowType != TYPE_QS_DIALOG) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
} else if (token.asActivityRecord() != null) {
attrs.token = null;
//创建 WindowToken
token = new WindowToken.Builder(this, client.asBinder(), type)
.setDisplayContent(displayContent)
.setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
.build();
}
//创建 WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
// 客户端进程已死亡
if (win.mDeathRecipient == null) {
return WindowManagerGlobal.ADD_APP_EXITING;
}
if (win.getDisplayContent() == null) {
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
//根据显示器策略调整窗口参数(如系统栏可见性)。
displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
win.updateRequestedVisibility(requestedVisibility);
//做最后的策略校验(如是否允许在锁屏上添加窗口等)
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != ADD_OKAY) {
return res;
}
//创建一对 `InputChannel`(服务端和客户端),通过 SocketPair 建立连接。
//服务端通道由 WMS 持有,用于向应用发送输入事件。
//客户端通道通过 `outInputChannel` 返回给应用进程(`ViewRootImpl` 使用)
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
if (type == TYPE_TOAST) {
//每个 UID 同时只能显示一个 Toast
if (!displayContent.canAddToastWindowForUid(callingUid)) {
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
if (addToastWindowRequiresToken
|| (attrs.flags & FLAG_NOT_FOCUSABLE) == 0
|| displayContent.mCurrentFocus == null
|| displayContent.mCurrentFocus.mOwnerUid != callingUid) {
//发送延迟消息,在 Toast 超时后自动隐藏窗口。
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
}
}
//WindowContext(Android 11+):允许在非 Activity 上下文中创建窗口
if (!win.isChildWindow()
&& mWindowContextListenerController.hasListener(windowContextToken)) {
// 校验窗口类型匹配
final int windowContextType = mWindowContextListenerController
.getWindowType(windowContextToken);
if (type != windowContextType) {
return WindowManagerGlobal.ADD_INVALID_TYPE;
}
final Bundle options = mWindowContextListenerController
.getOptions(windowContextToken);
// 注册监听器,使 WindowToken 能响应配置变化
mWindowContextListenerController.registerWindowContainerListener(
windowContextToken, token, callingUid, type, options);
}
res = ADD_OKAY;
//BLAST(Buffered Layer Stack):Android 12 引入的新渲染架构标志
if (mUseBLAST) {
res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
}
//如果当前没有焦点窗口,记录此窗口
if (displayContent.mCurrentFocus == null) {
displayContent.mWinAddedSinceNullFocus.add(win);
}
//某些窗口类型(如系统窗口)不参与点击外部关闭 Task 的判断
if (excludeWindowTypeFromTapOutTask(type)) {
displayContent.mTapExcludedWindows.add(win);
}
//将窗口与 DisplayContent 关联,并设置 SurfaceControl
win.attach();
//将窗口注册到 WMS 的全局映射表中
mWindowMap.put(client.asBinder(), win);
...
final ActivityRecord tokenActivity = token.asActivityRecord();
//设置 Starting Window
if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {
tokenActivity.mStartingWindow = win;
}
boolean imMayMove = true;
//将窗口添加到 `WindowToken` 的窗口列表中
win.mToken.addWindow(win);
//通知 `DisplayPolicy` 添加窗口(更新系统 UI 布局,如状态栏、导航栏)。
displayPolicy.addWindowLw(win, attrs);
//输入法窗口处理
if (type == TYPE_INPUT_METHOD) {
//设置当前输入法窗口
displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
//计算输入法目标
displayContent.computeImeTarget(true /* updateImeTarget */);
imMayMove = false;
} else {
//壁纸窗口处理
if (type == TYPE_WALLPAPER) {
//标记需要重新布局壁纸 displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (win.hasWallpaper()) {
////标记需要重新布局壁纸
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
final WindowStateAnimator winAnimator = win.mWinAnimator;
//标记需要执行入场动画
winAnimator.mEnterAnimationPending = true;
//标记窗口是否正在执行入场动画
winAnimator.mEnteringAnimation = true;
//检查当前 Activity 是否有"窗口替换"的需求(即 `mReplacingWindow` 不为空)
//,如果有,则创建一个 `WindowTransition` 来驱动新旧窗口的过渡动画
if (activity != null && activity.isVisible()
&& !prepareWindowReplacementTransition(activity)) {
//它会检查 Activity 是否处于"正在重启"状态(`mRelaunching` 标志),
//如果是,则确保一个 **"空过渡"(None Transition)** 被设置。这个"空过
//渡"的目的是让 WMS 知道"这个 Activity 要重新绘制,需要等待它的窗口绘制
//完成后再显示",防止出现白屏或闪屏。
prepareNoneTransitionForRelaunching(activity);
}
...
//标记输入窗口需要更新
//`InputMonitor` 是 WMS 中管理**输入系统状态**的核心类,
//负责维护所有窗口的输入信息,并与系统的 `InputDispatcher`(输入分发器)通信
displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();
boolean focusChanged = false;
if (win.canReceiveKeys()) {
//更新焦点窗口
//焦点变化会影响输入法显示、按键分发等
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
if (imMayMove) {
//计算输入法目标:如果没有焦点变化,仍可能需要重新计算输入法目标窗口
displayContent.computeImeTarget(true /* updateImeTarget */);
}
//计算窗口的 Z-order 层级
win.getParent().assignChildLayers();
if (focusChanged) {
//通知InputMonitor更新输入焦点 displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
false /*updateInputWindows*/);
}
//通知系统输入子系统(InputFlinger)更新窗口信息(用于触摸事件分发)
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
//如果窗口可见且屏幕方向需要变化,发送配置变更通知
if (win.isVisibleOrAdding() && displayContent.updateOrientation()) {
displayContent.sendNewConfiguration();
}
// 新系统栏(状态栏、导航栏)的 Insets 信息
displayContent.getInsetsStateController().updateAboveInsetsState(
win, false /* notifyInsetsChanged */);
//通过 `outActiveControls` 将控制信息返回给应用
getInsetsSourceControls(win, outActiveControls);
}
//恢复调用者身份
Binder.restoreCallingIdentity(origId);
return res;
}
2. 绘制流程的引擎:scheduleTraversals 与 VSync
绘制流程的启动,实际上是由 requestLayout() 或 invalidate() 等操作触发的,它们最终都会调用到 scheduleTraversals() 方法。scheduleTraversals() 的核心工作包括:
- 发送同步屏障:在 Handler 的消息队列中插入一个同步屏障,保证后续的绘制消息(异步消息)能被优先处理,提高 UI 响应速度。
- 注册 VSync 回调 :向
Choreographer(编舞者)注册一个CALLBACK_TRAVERSAL类型的回调。Choreographer负责接收系统的垂直同步(VSync)信号,并按照输入 > 动画 > 遍历(绘制) 的优先级顺序执行回调。 - 等待信号 :当下一个 VSync 信号到来时,
Choreographer就会执行之前注册的mTraversalRunnable,进而调用到doTraversal(),最终触发整个绘制流程的核心方法performTraversals()。
scss
//frameworks/base/core/java/android/view/ViewRootImpl.java
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
...
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//MessageQueue 中插入一个同步屏障 。这个屏障的作用是:
//所有同步消息都不会被执行,只有异步消息可以绕过屏障继续执行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//注册一个执行 View 的测量、布局、绘制的CallBack
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
//通知 `ThreadedRenderer`(硬件加速渲染器)即将有新的帧需要渲染。
//`ThreadedRenderer` 是 Android 硬件加速的核心组件,负责将绘制指令转换为 GPU 命令
notifyRendererOfFramePending();
//Android 系统有一个"Draw Lock"机制,用于防止设备在应用正在
//绘制关键帧时进入休眠
pokeDrawLockIfNeeded();
}
}
//VSYNC信号请求:Choreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL)
//具体调用看我的文章 # android 12 中的VSYNC请求
private void scheduleVsyncLocked() {
...
//mDisplayEventReceiver = FrameDisplayEventReceiver
//它通过 JNI 向系统的 `SurfaceFlinger` 注册 VSync 监听。
mDisplayEventReceiver.scheduleVsync();
...
}
//VSync 信号到达 → `onVsync()` → `doFrame()` → `doCallbacks()`
//具体调用看我的文章 # android 12 中的VSYNC请求
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
//移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
//核心绘制流程
performTraversals();
...
}
}
3. 三大流程:performTraversals 详解
performTraversals() 是 ViewRootImpl 最核心的方法,它会按顺序执行一次完整的 View 树刷新:
测量 (Measure) :performMeasure() , 从根 DecorView 开始,自上而下地递归调用每个子 View 的 measure() 方法,以确定每个 View 应该占据的宽度和高度。
布局 (Layout) :performLayout() ,在上一步确定好尺寸后,自顶向下地递归调用每个 View 的 layout() 方法,确定每个 View 在父容器中的具体位置。
绘制 (Draw) :performDraw() ,将 View 树的内容绘制到屏幕上。在硬件加速开启的情况下(Android 默认开启),它会通过 ThreadedRenderer 将绘制指令交给 GPU 处理,否则会通过 CPU 进行软件绘制。
4. 输入事件的中枢:InputStage 责任链
所有触摸、按键事件由系统 InputFlinger 分发给应用进程,ViewRootImpl 是应用内接收事件的入口。它内部设计了一套名为 InputStage 的责任链来处理事件。
- 当
ViewRootImpl的InputChannel收到原始事件后,会被封装成QueuedInputEvent放入队列。 - 随后,
doProcessInputEvents()方法会从队列中取出事件,并通过deliverInputEvent()方法将其交给责任链的第一个节点(mFirstInputStage)处理。 InputStage链上的每个节点(如ViewPreImeInputStage、ViewPostImeInputStage)各司其职,依次处理事件的不同阶段。如果某个Stage消费了事件,链条就会终止;否则,事件会继续传递给下一个Stage。最终,事件会被分发给具体的目标 View。
输入事件怎么分发的具体看我的文章# android12 InputManagerService分析之输入事件分发