ViewRootImpl 是 Android 视图系统中一个极其核心的内部类,它不是一个普通的 View,而是连接窗口管理器(WindowManager)和 View 树的“枢纽” 。你可以把它理解为一个窗口的“总管家”,负责驱动整个 View 树的测量、布局、绘制,以及接收和分发所有的输入事件。
1. 入口与创建:从 WindowManager.addView 开始
每个需要显示在屏幕上的窗口(如 Activity、Dialog、Toast),最终都会通过 WindowManager.addView() 将 DecorView(视图树的根)添加到系统中。
-
WindowManagerGlobal会为这个窗口创建一个ViewRootImpl实例。 -
随后调用其
setView()方法,这一步至关重要:- 保存 View 根:将传入的
DecorView赋值给ViewRootImpl的mView成员变量,作为后续操作的对象。 - 向系统添加窗口:通过
mWindowSession.addToDisplayAsUser()向系统服务WindowManagerService(WMS) 请求添加一个窗口。 - 请求首次布局:调用
requestLayout()请求首次布局。 - 初始化输入通道:建立一个
InputChannel,为后续接收来自系统InputFlinger(是 IMS 在 Native 层(C++) 的核心处理逻辑) 的输入事件做准备。
- 保存 View 根:将传入的
//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);
...
}
}
}
建立通道
-
WindowManagerService创建InputChannel
当窗口添加或更新时(ViewRootImpl.setView()),WMS 会通过InputManagerService创建一个InputChannel(这是一个用于进程间通信的 Socket/管道对)。其中一个通道(Server 端)留在 SystemServer 进程供 IMS 写入事件,另一个通道(Client 端)通过 Binder 返回给应用进程。 -
应用端注册接收器
ViewRootImpl拿到 Client 端的InputChannel后,会创建WindowInputEventReceiver(继承自InputEventReceiver),并调用其构造函数。
在父类InputEventReceiver的构造函数中,会调用nativeInit-> 在 Native 层创建NativeInputEventReceiver对象 -> 最终调用的InputChannel.setNativeInputChannel,将 Native 层的InputChannel指针保存到 Java 层的mPtr中。 -
Native 层开始监听
绑定完成后,Native 层的NativeInputEventReceiver会通过Looper(或epoll)开始监听这个InputChannel的文件描述符(fd)。当 IMS 将触摸或按键事件写入该通道时,Native 层会立即收到回调,并触发InputEventReceiver.dispatchInputEvent(),进而一步步走到ViewRootImpl.deliverInputEvent()。
//frameworks/base/services/java/com/android/server/wm/WindowState.java
//在IMS中创建InputChannel
void openInputChannel(InputChannel outInputChannel) {
...
mInputChannel = mWmService.mInputManager.createInputChannel(name);
mInputChannelToken = mInputChannel.getToken();
mInputWindowHandle.setToken(mInputChannelToken);
mWmService.mInputToWindowMap.put(mInputChannelToken, this);
if (outInputChannel != null) {
mInputChannel.copyTo(outInputChannel);
} else {
...
}
}
//frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
public InputChannel createInputChannel(String name) {
return nativeCreateInputChannel(mPtr, name);
}
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jobject nativeCreateInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr,
jstring nameObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
...
base::Result<std::unique_ptr<InputChannel>> inputChannel = im->createInputChannel(env, name);
...
return inputChannelObj;
}
//frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
JNIEnv* /* env */, const std::string& name) {
ATRACE_CALL();
return mInputManager->getDispatcher()->createInputChannel(name);
}
//frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
//创建一对 `InputChannel` ,一个传给客户端(通过 `outInputChannel`),
//一个保留在 WMS 中用于接收输入事件
Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const std::string& name) {
std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
//创建 Socket 管道
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
{ // acquire lock
std::scoped_lock _l(mLock);
const sp<IBinder>& token = serverChannel->getConnectionToken();
int fd = serverChannel->getFd();
//构建 Connection 对象
sp<Connection> connection =
new Connection(std::move(serverChannel), false /*monitor*/, mIdGenerator);
if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) {
}
//缓存起来,使用 `emplace` 插入,避免重复键
mConnectionsByToken.emplace(token, connection);
std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback,
this, std::placeholders::_1, token);
//注册 Looper 回调
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return clientChannel;
}
public void copyTo(InputChannel outParameter) {
...
//将 Java 层的对象与 Native(C++)层的输入管道绑定起来。
outParameter.setNativeInputChannel(nativeDup(mPtr));
}
2. 绘制流程的引擎:scheduleTraversals 与 VSync
绘制流程的启动,实际上是由 requestLayout() 或 invalidate() 等操作触发的,它们最终都会调用到 scheduleTraversals() 方法。scheduleTraversals() 的核心工作包括:
- 发送同步屏障:在 Handler 的消息队列中插入一个同步屏障,保证后续的绘制消息(异步消息)能被优先处理,提高 UI 响应速度。
- 注册 VSync 回调:向
Choreographer(编舞者)注册一个CALLBACK_TRAVERSAL类型的回调。Choreographer负责接收系统的垂直同步(VSync)信号,并按照输入 > 动画 > 遍历(绘制) 的优先级顺序执行回调。 - 等待信号:当下一个 VSync 信号到来时,
Choreographer就会执行之前注册的mTraversalRunnable,进而调用到doTraversal(),最终触发整个绘制流程的核心方法performTraversals()。
//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请求
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
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 进行软件绘制。
窗口布局 (relayoutWindow) :relayoutWindow() 通过Binder调用WMS(WindowManagerService),请求更新窗口尺寸、Surface等。
private void performTraversals() {
final View host = mView;
//如果根视图不存在,或者该ViewRootImpl还没有被添加到WindowManager
if (host == null || !mAdded) {
return;
}
//BLAST (BufferQueue Layer Architecture Sync):这是Android图形系统的一个同步机制
if (mWaitForBlastSyncComplete) {
//如果`mWaitForBlastSyncComplete`为`true`,意味着ViewRootImpl正在
//等待上一帧的BufferQueue事务被SurfaceFlinger处理完成。为了防止在等待期间
//再次请求绘制导致状态混乱,此方法会直接返回,并设置
//`mRequestedTraverseWhilePaused`为`true`,
//以便在同步完成后立即重新触发一次遍历
mRequestedTraverseWhilePaused = true;
return;
}
//标记当前正处于遍历过程中,防止重入
mIsInTraversal = true;
//表示即将进行绘制
mWillDrawSoon = true;
//用于后续判断窗口大小是否可能改变
boolean windowSizeMayChange = false;
WindowManager.LayoutParams lp = mWindowAttributes;
//期望的窗口宽高
int desiredWindowWidth;
int desiredWindowHeight;
//获取根视图当前的可见性状态。
final int viewVisibility = getHostVisibility();
//视图可见性是否发生了变化
final boolean viewVisibilityChanged = !mFirst
&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded
|| mAppVisibilityChanged);
mAppVisibilityChanged = false;
//视图是否在"可见"和"不可见"之间发生了切换
final boolean viewUserVisibilityChanged = !mFirst &&
((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
WindowManager.LayoutParams params = null;
...
Rect frame = mWinFrame;
//第一次遍历
if (mFirst) {
//强制要求完整重绘
mFullRedrawNeeded = true;
//强制要求重新布局
mLayoutRequested = true;
final Configuration config = getConfiguration();
//如果窗口应该使用Display的真实尺寸(系统窗口)
if (shouldUseDisplaySize(lp)) {
//期望的窗口尺寸=屏幕真实大小
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
//如果布局参数是`WRAP_CONTENT`
} else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
//期望的窗口尺寸使用屏幕的DP尺寸(转换为像素)
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
} else {
//直接使用`mWinFrame`(来自WindowManager的窗口尺寸)的尺寸
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
}
//设置`AttachInfo`的初始状态
mAttachInfo.mUse32BitDrawingCache = true;
mAttachInfo.mWindowVisibility = viewVisibility;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfigurationFromResources.setTo(config);
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
//如果布局方向未设置,则从配置中获取并设置
if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
host.setLayoutDirection(config.getLayoutDirection());
}
//通知根视图它已经被附加到窗口。这是View生命周期的关键一步。
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
//分发窗口Insets(如状态栏、导航栏区域)
dispatchApplyInsets(host);
} else {
//非首次遍历,期望的窗口尺寸直接使用当前的`mWinFrame`尺寸
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
//当前窗口帧的尺寸与上次记录的尺寸(`mWidth`, `mHeight`)不同,
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
//设置重绘、布局标志,并标记`windowSizeMayChange = true`。
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
//如果视图可见性发生了变化
if (viewVisibilityChanged) {
//更新`AttachInfo`中的可见性
mAttachInfo.mWindowVisibility = viewVisibility;
//分发窗口可见性变化事件给根视图,最终会传递到各个子View。
host.dispatchWindowVisibilityChanged(viewVisibility);
//如果用户可见性发生变化,分发聚合可见性变化
if (viewUserVisibilityChanged) {
host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
}
//如果变为不可见,或者需要新Surface,
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
//则结束拖拽调整大小状态
endDragResizing();
//并销毁硬件渲染资源
destroyHardwareResources();
}
}
//如果窗口不可见
if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
//清除其可能持有的无障碍焦点
host.clearAccessibilityFocus();
}
//执行在运行队列(`RunQueue`)中排队的动作。这些动作通常是由已分离的View
//在`Handler`上延迟执行的。
getRunQueue().executeActions(mAttachInfo.mHandler);
//确定是否需要布局
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
final Resources res = mView.getContext().getResources();
//首次遍历
if (mFirst) {
//设置触摸模式
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
} else {
//不是首次遍历,
//并且布局参数是`WRAP_CONTENT`
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
//那么窗口大小可能改变
windowSizeMayChange = true;
//并重新计算期望的窗口尺寸(基于Display大小或屏幕DP)
if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
Configuration config = res.getConfiguration();
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
}
}
//measureHierarchy这个方法会考虑窗口的`LayoutParams`(如`WRAP_CONTENT`、
//`MATCH_PARENT`或固定尺寸)以及`desiredWindowWidth/Height`,最终调用
//`performMeasure()`来执行实际的测量操作。
//`measureHierarchy()`的返回值会与`windowSizeMayChange`进行或运算,
//表示测量过程可能导致窗口尺寸发生变化。
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
}
//检查视图树中是否有View请求了特定的窗口属性
if (collectViewAttributes()) {
params = lp;
}
if (mAttachInfo.mForceReportNewAttributes) {
mAttachInfo.mForceReportNewAttributes = false;
params = lp;
}
if (mFirst || mAttachInfo.mViewVisibilityChanged) {
mAttachInfo.mViewVisibilityChanged = false;
//处理软键盘输入模式
int resizeMode = mSoftInputMode & SOFT_INPUT_MASK_ADJUST;
if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
final int N = mAttachInfo.mScrollContainers.size();
for (int i=0; i<N; i++) {
if (mAttachInfo.mScrollContainers.get(i).isShown()) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
}
if (resizeMode == 0) {
resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
}
if ((lp.softInputMode & SOFT_INPUT_MASK_ADJUST) != resizeMode) {
lp.softInputMode = (lp.softInputMode & ~SOFT_INPUT_MASK_ADJUST) | resizeMode;
params = lp;
}
}
}
//如果有待处理的Insets应用请求 (`mApplyInsetsRequested`),
//并且窗口没有在移动或调整大小
if (mApplyInsetsRequested && !(mWillMove || mWillResize)) {
//立即分发Insets (`dispatchApplyInsets`)。
dispatchApplyInsets(host);
if (mLayoutRequested) {
//如果分发Insets后触发了新的布局请求,则再次执行`measureHierarchy()`
//,因为Insets的变化可能会影响View的测量尺寸
windowSizeMayChange |= measureHierarchy(host, lp,
mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
}
}
//清除布局请求标志。注意,后续代码如果再次设置此标志,会导致额外的布局操作
if (layoutRequested) {
mLayoutRequested = false;
}
//判断是否需要向WindowManager请求调整窗口大小
//有布局请求 (`layoutRequested`) 且窗口大小可能变化(`windowSizeMayChange`)
//并且 (View的测量尺寸与当前窗口尺寸不匹配,或者对于`WRAP_CONTENT`的宽/高,当前的窗口帧小于期望的尺寸且不等于当前记录的尺寸)
//如果正处于自由形式的拖拽调整大小状态 (`mDragResizing &&
//RESIZE_MODE_FREEFORM`),也认为需要调整。
boolean windowShouldResize = layoutRequested && windowSizeMayChange
&& ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight())
|| (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.width() < desiredWindowWidth && frame.width() != mWidth)
|| (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
frame.height() < desiredWindowHeight && frame.height() != mHeight));
windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;
//如果Activity被重新启动 (`mActivityRelaunched`),也需要调整。
windowShouldResize |= mActivityRelaunched;
//计算`computesInternalInsets`:是否有监听器需要计算内部Insets,
//或者上一次计算的Insets非空
final boolean computesInternalInsets =
mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
|| mAttachInfo.mHasNonEmptyGivenInternalInsets;
//初始化一系列用于记录`relayoutWindow`结果的标志
boolean insetsPending = false;
int relayoutResult = 0;
boolean updatedConfiguration = false;
//获取当前Surface的generation ID,用于后续判断Surface是否被替换
final int surfaceGenerationId = mSurface.getGenerationId();
//记录视图是否可见
final boolean isViewVisible = viewVisibility == View.VISIBLE;
final boolean windowRelayoutWasForced = mForceNextWindowRelayout;
boolean surfaceSizeChanged = false;
boolean surfaceCreated = false;
boolean surfaceDestroyed = false;
boolean surfaceReplaced = false;
//检查窗口属性是否发生变化,如果变化,则设置`params`以便在下一次`relayoutWindow`
//中更新
final boolean windowAttributesChanged = mWindowAttributesChanged;
if (windowAttributesChanged) {
mWindowAttributesChanged = false;
params = lp;
}
//如果`params`需要更新
if (params != null) {
//如果View请求透明区域但窗口格式不支持Alpha通道,则强制改为半透明格式
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0
&& !PixelFormat.formatHasAlpha(params.format)) {
params.format = PixelFormat.TRANSLUCENT;
}
//对`LayoutParams`进行兼容性调整
adjustLayoutParamsForCompatibility(params);
controlInsetsForCompatibility(params);
//如果系统栏外观发生变化,通知View
if (mDispatchedSystemBarAppearance != params.insetsFlags.appearance) {
mDispatchedSystemBarAppearance = params.insetsFlags.appearance;
mView.onSystemBarAppearanceChanged(mDispatchedSystemBarAppearance);
}
}
final boolean wasReportNextDraw = mReportNextDraw;
//### 执行窗口重布局 (relayoutWindow)
//首次遍历、窗口需要调整大小、可见性变化、参数变化或强制重布局
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
|| mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
//设置`insetsPending`,告知WindowManager我们稍后会提供内部Insets
insetsPending = computesInternalInsets;
//如果持有`SurfaceHolder`(例如SurfaceView)
if (mSurfaceHolder != null) {
//则加锁并允许绘制
mSurfaceHolder.mSurfaceLock.lock();
mDrawingAllowed = true;
}
boolean hwInitialized = false;
boolean dispatchApplyInsets = false;
//记录是否之前有有效的Surface (`hadSurface`)
boolean hadSurface = mSurface.isValid();
try {
//如果硬件渲染器存在,
if (mAttachInfo.mThreadedRenderer != null) {
//暂停渲染
if (mAttachInfo.mThreadedRenderer.pause()) {
//`pause()`返回`true`,说明有动画正在运行,
//需要推出一帧来恢复它们,因此将脏区域设置为整个窗口
mDirty.set(0, 0, mWidth, mHeight);
}
}
//如果是首次遍历或可见性发生变化
if (mFirst || viewVisibilityChanged) {
mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED;
}
//这是一个IPC调用(通过`IWindowSession`),它会与WindowManagerService
//(WMS) 通信,请求重新布局窗口
//`relayoutWindow`:更新窗口帧 (`mWinFrame`)、
//更新Surface尺寸、创建或销毁Surface、应用窗口动画、返回合并后的配置
//(`mPendingMergedConfiguration`) 等。其返回值 `relayoutResult`
//包含了各种状态标志。
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
//判断窗口是否处于拖拽调整大小模式
//自由窗口模式(如桌面模式下的窗口)
final boolean freeformResizing = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0;
//分屏模式
final boolean dockedResizing = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED) != 0;
//任意一种拖拽调整大小模式
final boolean dragResizing = freeformResizing || dockedResizing;
//如果`relayoutResult`包含`RELAYOUT_RES_BLAST_SYNC`标志,
//说明这次重布局需要与BLAST同步。
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC) != 0) {
//标记下一次绘制
reportNextDraw();
//如果硬件渲染开启
if (isHardwareEnabled()) {
//表示下一次绘制使用BLAST同步机制
mNextDrawUseBlastSync = true;
}
}
//检查SurfaceControl是否发生了变化。
final boolean surfaceControlChanged =
(relayoutResult & RELAYOUT_RES_SURFACE_CHANGED)
== RELAYOUT_RES_SURFACE_CHANGED;
//如果SurfaceControl有效
if (mSurfaceControl.isValid()) {
//会根据窗口属性、是否拖拽调整大小、Surface是否变化来更新透明度
updateOpacity(mWindowAttributes, dragResizing,
surfaceControlChanged /*forceUpdate */);
}
...
surfaceSizeChanged = false;
//检查Surface尺寸是否发生了变化
if (!mLastSurfaceSize.equals(mSurfaceSize)) {
surfaceSizeChanged = true;
mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y);
}
//检查"总是消费系统栏"的配置是否发生了变化。这个标志影响系统栏事件的分发方式
final boolean alwaysConsumeSystemBarsChanged =
mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars;
//如果窗口的颜色模式(如广色域、HDR等)发生变化,更新渲染器的颜色模式设置
updateColorModeIfNeeded(lp.getColorMode());
//`surfaceCreated`:之前没有Surface,现在有了有效的Surface
//`surfaceDestroyed`:之前有Surface,现在无效了
surfaceCreated = !hadSurface && mSurface.isValid();
surfaceDestroyed = hadSurface && !mSurface.isValid();
//如果Surface的generation ID变化了,或者SurfaceControl发生了变化,
//并且当前Surface有效,则认为Surface被替换了。
surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId()
|| surfaceControlChanged) && mSurface.isValid();
if (surfaceReplaced) {
//增加序列号,用于追踪Surface的变化
mSurfaceSequenceId++;
}
//如果"总是消费系统栏"配置变化,
if (alwaysConsumeSystemBarsChanged) {
//更新`AttachInfo`并标记需要分发Insets
mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
dispatchApplyInsets = true;
}
//如果标题栏Insets发生变化(例如窗口标题栏的位置变化),
if (updateCaptionInsets()) {
//标记需要分发Insets
dispatchApplyInsets = true;
}
//如果需要分发Insets(配置变化、系统UI可见性变化、或有待处理的Insets请求),
if (dispatchApplyInsets || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) {
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
dispatchApplyInsets(host);
dispatchApplyInsets = true;
}
//**Surface创建**
if (surfaceCreated) {
//标记需要完全重绘,清空之前记录的透明区域。
mFullRedrawNeeded = true;
mPreviousTransparentRegion.setEmpty();
//如果硬件渲染器存在
if (mAttachInfo.mThreadedRenderer != null) {
try {
//初始化
hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface);
//如果初始化成功,并且视图没有请求透明区域
if (hwInitialized && (host.mPrivateFlags
& View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
//预分配渲染缓冲区
mAttachInfo.mThreadedRenderer.allocateBuffers();
}
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
} //**Surface销毁**
} else if (surfaceDestroyed) {
//清除滚动焦点
if (mLastScrolledFocus != null) {
mLastScrolledFocus.clear();
}
//重置滚动位置
mScrollY = mCurScrollY = 0;
//如果根视图实现了`RootViewSurfaceTaker`
if (mView instanceof RootViewSurfaceTaker) {
//通知滚动位置变化
((RootViewSurfaceTaker)
mView).onRootViewScrollYChanged(mCurScrollY);
}
if (mScroller != null) {
//终止滚动动画
mScroller.abortAnimation();
}
//如果硬件渲染开启,
if (isHardwareEnabled()) {
//销毁渲染器
mAttachInfo.mThreadedRenderer.destroy();
}
//如果Surface被替换、尺寸变化、或强制重布局,并且没有
//`SurfaceHolder`,硬件渲染器存在,Surface有效
} else if ((surfaceReplaced
|| surfaceSizeChanged || windowRelayoutWasForced)
&& mSurfaceHolder == null
&& mAttachInfo.mThreadedRenderer != null
&& mSurface.isValid()) {
//标记需要完全重绘
mFullRedrawNeeded = true;
try {
//更新Surface
mAttachInfo.mThreadedRenderer.updateSurface(mSurface);
} catch (OutOfResourcesException e) {
handleOutOfResourcesException(e);
return;
}
}
//拖拽调整大小状态处理
if (mDragResizing != dragResizing) {
//如果是拖拽调整大小模式
if (dragResizing) {
//设置`mResizeMode`为自由窗口模式或分屏模式
mResizeMode = freeformResizing
? RESIZE_MODE_FREEFORM
: RESIZE_MODE_DOCKED_DIVIDER;
//检查背景框尺寸是否与窗口帧匹配
final boolean backdropSizeMatchesFrame =
mWinFrame.width() == mPendingBackDropFrame.width()
&& mWinFrame.height() == mPendingBackDropFrame.height();
// 开始拖拽调整大小,传入背景框、是否匹配、内容Insets、
//稳定Insets和调整模式
startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mResizeMode);
} else {
//如果退出拖拽调整大小模式
endDragResizing();
}
}
//如果使用的是非移动端渲染器
if (!mUseMTRenderer) {
if (dragResizing) {
//设置Canvas偏移量为窗口左上角坐标,确保绘制内容正确偏移
mCanvasOffsetX = mWinFrame.left;
mCanvasOffsetY = mWinFrame.top;
} else {
//重置偏移量为0
mCanvasOffsetX = mCanvasOffsetY = 0;
}
}
} catch (RemoteException e) {
}
//更新`AttachInfo`中的窗口位置
mAttachInfo.mWindowLeft = frame.left;
mAttachInfo.mWindowTop = frame.top;
//如果与之前不同则更新 ,更新记录的窗口尺寸`mWidth`和`mHeight`
if (mWidth != frame.width() || mHeight != frame.height()) {
mWidth = frame.width();
mHeight = frame.height();
}
if (mSurfaceHolder != null) {
//如果Surface有效,将Surface赋值给`SurfaceHolder`
if (mSurface.isValid()) {
mSurfaceHolder.mSurface = mSurface;
}
//设置Surface的尺寸为窗口尺寸
mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);
mSurfaceHolder.mSurfaceLock.unlock();
//如果Surface刚刚创建
if (surfaceCreated) {
//确保回调不会被重复调用
mSurfaceHolder.ungetCallbacks();
mIsCreating = true;
SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
//遍历并调用每个回调的`surfaceCreated()`方法,通知应用Surface已创建
c.surfaceCreated(mSurfaceHolder);
}
}
}
//如果Surface创建、替换、尺寸变化或窗口属性变化,并且Surface有效
if ((surfaceCreated || surfaceReplaced || surfaceSizeChanged
|| windowAttributesChanged) && mSurface.isValid()) {
SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
//遍历所有回调,调用`surfaceChanged()`方法,
//通知应用Surface发生了变化(格式、宽高)
c.surfaceChanged(mSurfaceHolder, lp.format,
mWidth, mHeight);
}
}
mIsCreating = false;
}
//如果Surface被销毁
if (surfaceDestroyed) {
//通知持有者Surface被销毁
notifyHolderSurfaceDestroyed();
mSurfaceHolder.mSurfaceLock.lock();
try {
//加锁并创建一个新的空的Surface对象替换原来的
mSurfaceHolder.mSurface = new Surface();
} finally {
mSurfaceHolder.mSurfaceLock.unlock();
}
}
}
final ThreadedRenderer threadedRenderer = mAttachInfo.mThreadedRenderer;
//检查是否需要设置/重新设置渲染器:硬件初始化、尺寸变化、或需要设置
if (threadedRenderer != null && threadedRenderer.isEnabled()) {
if (hwInitialized
|| mWidth != threadedRenderer.getWidth()
|| mHeight != threadedRenderer.getHeight()
|| mNeedsRendererSetup) {
//配置渲染器,传入窗口尺寸、AttachInfo和Surface Insets
threadedRenderer.setup(mWidth, mHeight, mAttachInfo,
mWindowAttributes.surfaceInsets);
mNeedsRendererSetup = false;
}
}
if (!mStopped || wasReportNextDraw) {
//表示触摸模式变化是否导致了焦点变化
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
//触摸模式导致焦点变化、窗口尺寸与测量尺寸不匹配、应用了Insets、或配置更新
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || dispatchApplyInsets ||
updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
//重新测量,会触发整个视图树的`onMeasure()`方法
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//获取测量后的宽高,并初始化`measureAgain`
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
//如果`LayoutParams`中设置了水平或垂直权重
if (lp.horizontalWeight > 0.0f) {
//根据剩余空间和权重比例计算额外的尺寸
width += (int) ((mWidth - width) * lp.horizontalWeight);
//创建精确模式的MeasureSpec
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
//- 标记需要再次测量
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
//如果需要再次测量(因为有权重)
if (measureAgain) {
//执行第二次测量。
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
} else {
//如果不满足任何重布局条件(没有首次、不需要调整大小、可见性没变、参数没变、
//没有强制重布局)
//检查窗口是否移动了位置,如果移动了则更新AttachInfo中的窗口位置。
maybeHandleWindowMove(frame);
}
//如果Surface尺寸变化、替换、创建或窗口属性变化,
if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
//会遍历窗口中的所有SurfaceView等子视图,更新它们的Surface相关状态
prepareSurfaces();
}
//是否需要执行布局。条件是有布局请求,并且应用未停止或需要报告下一次绘制。
final boolean didLayout = layoutRequested && (!mStopped || wasReportNextDraw);
//是否需要触发全局布局监听器。布局执行了,或者需要重新计算全局属性。
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
//如果需要布局
if (didLayout) {
//执行布局
//此方法会调用根视图的`layout()`方法,从而触发整个视图树的布局过程(`onLayout`)
performLayout(lp, mWidth, mHeight);
//如果视图请求透明区域(`PFLAG_REQUEST_TRANSPARENT_REGIONS`)
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
//获取视图在窗口中的位置(
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
//遍历视图树,收集所有子视图的透明区域。这个过程会从`mTransparentRegion`中
//减去不透明子视图的区域,最终得到真正透明的区域。
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
//如果有坐标转换器(例如在兼容模式下),将透明区域从窗口坐标转换为屏幕坐标
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
//如果透明区域发生了变化
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
//更新`mPreviousTransparentRegion`
mPreviousTransparentRegion.set(mTransparentRegion);
//标记需要完全重绘
mFullRedrawNeeded = true;
//- 获取SurfaceControl,通过SurfaceTransaction设置透明区域提示
//给SurfaceFlinger,帮助优化合成性能
SurfaceControl sc = getSurfaceControl();
if (sc.isValid()) {
mTransaction.setTransparentRegionHint(sc, mTransparentRegion).apply();
}
}
}
}
//**通知Surface生命周期事件**
if (surfaceCreated) {
notifySurfaceCreated();
} else if (surfaceReplaced) {
notifySurfaceReplaced();
} else if (surfaceDestroyed) {
notifySurfaceDestroyed();
}
//触发全局布局监听器
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
//计算并报告内部Insets
if (computesInternalInsets) {
final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets;
insets.reset();
mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
mAttachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty();
// Tell the window manager.
if (insetsPending || !mLastGivenInsets.equals(insets)) {
mLastGivenInsets.set(insets);
final Rect contentInsets;
final Rect visibleInsets;
final Region touchableRegion;
if (mTranslator != null) {
contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets);
visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets);
touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion);
} else {
contentInsets = insets.contentInsets;
visibleInsets = insets.visibleInsets;
touchableRegion = insets.touchableRegion;
}
try {
//通过`mWindowSession.setInsets()`将计算出的内部Insets
//报告给WindowManagerService。
mWindowSession.setInsets(mWindow, insets.mTouchableInsets,
contentInsets, visibleInsets, touchableRegion);
} catch (RemoteException e) {
}
}
}
//在首次遍历时处理焦点分配
if (mFirst) {
//如果`AlwaysAssignFocus`为true或者不在触摸模式
if (sAlwaysAssignFocus || !isInTouchMode()) {
if (mView != null) {
//如果根视图没有焦点
if (!mView.hasFocus()) {
//尝试恢复默认焦点
mView.restoreDefaultFocus();
}
} else {
}
}
} else {
//(在触摸模式下)
//查找当前焦点的View
View focused = mView.findFocus();
//如果焦点View是一个ViewGroup,并且其焦点策略是
//`FOCUS_AFTER_DESCENDANTS`(先给子View
if (focused instanceof ViewGroup
&& ((ViewGroup) focused).getDescendantFocusability()
== ViewGroup.FOCUS_AFTER_DESCENDANTS) {
//则尝试恢复默认焦点这是为了处理ScrollView等在布局完成后
//才可能让子View获得焦点的情况
focused.restoreDefaultFocus();
}
}
}
//可见性发生了变化(首次遍历或可见性变化)并且当前可见
final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
//窗口是否有焦点并且视图可见
final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible;
final boolean regainedFocus = hasWindowFocus && mLostWindowFocus;
//重新获得焦点(有窗口焦点并且之前失去焦点)
if (regainedFocus) {
mLostWindowFocus = false;
} else if (!hasWindowFocus && mHadWindowFocus) {
mLostWindowFocus = true;
}
if (changedVisibility || regainedFocus) {
//如果可见性变化或重新获得焦点,并且不是Toast窗口,
boolean isToast = mWindowAttributes.type == TYPE_TOAST;
if (!isToast) {
//发送无障碍事件`TYPE_WINDOW_STATE_CHANGED`。
host.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
}
mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mActivityRelaunched = false;
mViewVisibility = viewVisibility;
mHadWindowFocus = hasWindowFocus;
mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
//如果`relayoutWindow()`返回`RELAYOUT_RES_FIRST_TIME`(表示这是第一次布局)
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
}
//### 执行绘制
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
//如果不取消绘制
if (!cancelDraw) {
//如果有待处理的View属性动画过渡(`ViewPropertyAnimator`)
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
//启动过渡动画
mPendingTransitions.get(i).startChangingAnimations();
}
//清空待处理过渡列表
mPendingTransitions.clear();
}
performDraw();
} else {
//如果取消绘制
//如果视图仍然可见
if (isViewVisible) {
//安排下一次遍历
scheduleTraversals();
} else {
//如果视图不可见
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
//结束所有待处理的过渡动画
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
//如果需要报告下一次绘制(`mReportNextDraw`为true)
if (!wasReportNextDraw && mReportNextDraw) {
mReportNextDraw = false;
//完成绘制
pendingDrawFinished();
}
}
}
...
mIsInTraversal = false;
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
...
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
//标记滚动可能发生变化
mScrollMayChange = true;
//表示当前正处于布局过程中
mInLayout = true;
final View host = mView;
if (host == null) {
return;
}
try {
//调用根视图的`layout()`方法。
//- 这里传入`(0, 0, measuredWidth, measuredHeight)`,
//表示根视图从窗口的`(0,0)`位置开始,尺寸为测量阶段确定的尺寸。
//这会触发整个视图树的递归布局过程,每个View的`onLayout()`方法会被调用,
//以确定所有子View的位置。
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
//立即将`mInLayout`设为`false`。这看起来有些反直觉,
//但目的是为了检测在`layout()`执行完毕后,是否有View在`onLayout()`
//或`onLayout()`之后的代码中调用了`requestLayout()`
//如果在`layout()`执行过程中有View调用了`requestLayout()`,
//它们会被添加到`mLayoutRequesters`列表中(在`requestLayout()`中通过
//`ViewRootImpl`的`requestLayoutDuringLayout()`方法添加)。
mInLayout = false;
//检查在布局过程中是否有View请求了重新布局(调用了`requestLayout()`)
//`mLayoutRequesters`是一个`ArrayList<View>`,存储了在布局过程中请求
//重新布局的View。
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
///遍历`mLayoutRequesters`列表,检查每个View的`mPrivateFlags`
//中是否设置了`PFLAG_FORCE_LAYOUT`标志。返回仍然有效的请求者列表
//(即`PFLAG_FORCE_LAYOUT`仍为`true`的View),同时会清除
//这些View的`PFLAG_FORCE_LAYOUT`标志(因为`false`参数表示需要清除)
ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
false);
if (validLayoutRequesters != null) {
//如果存在有效的布局请求者
mHandlingLayoutInLayoutRequest = true;
int numValidRequests = validLayoutRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
//遍历所有有效的布局请求者
final View view = validLayoutRequesters.get(i);
//再次调用`view.requestLayout()`。这会重新设置
//`PFLAG_FORCE_LAYOUT`标志,并重新将View添加到
//`mLayoutRequesters`列表中,为第二次布局做准备。
view.requestLayout();
}
//执行第二次测量:由于某些View在布局过程中请求了重新布局
//,需要重新执行测量阶段。
//调用`measureHierarchy()`,传入窗口参数、资源和期望的宽高。
//这会导致整个视图树重新测量,以确保尺寸正确。
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
//表示进入第二次布局过程。
mInLayout = true;
//执行第二次布局。这次会使用重新测量后的尺寸
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
//表示第二次布局过程结束
mHandlingLayoutInLayoutRequest = false;
//检查第二次布局过程中是否有新的请求
//参数`true`表示**只检查但不清除**`PFLAG_FORCE_LAYOUT`标志。
//如果第二次布局过程中又有View调用了`requestLayout()`,
//它们会再次出现在`mLayoutRequesters`中。
validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
// 如果在第二次布局过程中仍有View调用了`requestLayout()`,
if (validLayoutRequesters != null) {
final ArrayList<View> finalRequesters = validLayoutRequesters;
//不立即处理(避免无限循环),而是通过`RunQueue`将请求延迟到下一帧执行
//`RunQueue`是ViewRootImpl内部的机制,用于在Handler上延迟执行操作。
getRunQueue().post(new Runnable() {
@Override
public void run() {
int numValidRequests = finalRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = finalRequesters.get(i);
view.requestLayout();
}
}
});
}
}
}
} finally {
}
//表示布局过程彻底结束。
mInLayout = false;
}
4. 输入事件的中枢:InputStage 责任链
所有触摸、按键事件由系统 InputFlinger 分发给应用进程,ViewRootImpl 是应用内接收事件的入口。它内部设计了一套名为 InputStage 的责任链来处理事件。
- 当
ViewRootImpl的InputChannel收到原始事件后,会被封装成QueuedInputEvent放入队列。 - 随后,
doProcessInputEvents()方法会从队列中取出事件,并通过deliverInputEvent()方法将其交给责任链的第一个节点(mFirstInputStage)处理。 InputStage链上的每个节点(如ViewPreImeInputStage、ViewPostImeInputStage)各司其职,依次处理事件的不同阶段。如果某个Stage消费了事件,链条就会终止;否则,事件会继续传递给下一个Stage。最终,事件会被分发给具体的目标 View。
输入事件怎么分发的具体看我的文章# android12 InputManagerService分析之输入事件分发