在上篇文章中分析了WMS显示流程中的添加窗口,本文将着重分析下布局窗口,即relayoutWindow流程。
本文以android 13代码为例进行源码分析。
经过之前的分析已经知道,在一个activity启动时,窗口显示的三部曲(Measure、layout、draw)的触发点都是在 ResumeActivityItem 事务执行到 ViewRootImpl::setView 方法触发的,我们就从 setView 开始进行分析。
1,在frameworks/base/core/java/android/view/ViewRootImpl.java中
java
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {
final IWindowSession mWindowSession;//与WMS通信的binder
final Choreographer mChoreographer;
final W mWindow;
View mView; //对应的DecorView
public final Surface mSurface = new Surface(); // 应用端这个View树的 Surface,这个mSurface是专门提供给docerview上面的canvas. 真正的 Suface 创建是在 system_service 端触发
private final SurfaceControl mSurfaceControl = new SurfaceControl(); // 对应的SurfaceControl
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {
synchronized (this) {
if (mView == null) { // mView为null,表示这是第一次设置视图
mView = view; // 将DecorView赋值给 mView
requestLayout(); // 在添加到窗口管理器之前请求首次布局,确保在接收其他系统事件之前完成重新布局。relayoutWindow和finishDrawingWindow都在这里触发
InputChannel inputChannel = null; // 用于窗口接收input
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { // 如果需要输入通道(没有设置INPUT_FEATURE_NO_INPUT_CHANNEL标志),则创建一个新的InputChannel。
inputChannel = new InputChannel();
}
// 通过binder通信,调用wms的addWindow方法,addToDisplayAsUser的第一个参数mWindow在ViewRootImpl的构造函数中初始化即 mWindow = new W(this);
// 调用的 Session::addToDisplayAsUser 最终执行的是 WindowManagerService:: addWindow
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
mTempControls); // 尝试将窗口添加到指定的显示和用户中。这一步涉及到多个参数,如窗口、窗口属性、可见性、显示ID、用户ID等
view.assignParent(this); // 将视图(view)的父级设置为当前窗口(this)。DecorView::getParent 返回的是 ViewRootImpl 的原因
}
}
}
}
上面的 setView 方法,执行了 requestLayout 方法,并进一步执行 scheduleTraversals 函数代码的目的是确保UI遍历(包括测量、布局和绘制)被调度执行。通过 Choreographer 来协调这个过程,并利用同步屏障来确保在遍历执行期间,消息队列中的其他同步消息不会干扰到遍历过程。
下面看下 requestLayout 函数。
scss
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread(); //只有主线程才能更新UI
mLayoutRequested = true;
scheduleTraversals(); //调用scheduleTraversals函数
}
}
上面的代码调用scheduleTraversals函数。
scss
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 通过消息循环(Looper)的队列(Queue)发布一个同步屏障(Sync Barrier)。这个屏障用于阻塞消息队列中同步消息的处理,直到屏障被移除。mTraversalBarrier变量保存了这个屏障的引用。
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 调用Choreographer对象的postCallback方法。Choreographer是Android系统中用于协调动画、输入和绘制的一个类。
// 向Choreographer注册一个回调。CALLBACK_TRAVERSAL是回调的类型,表示这是一个遍历回调。
// mTraversalRunnable是一个实现了遍历逻辑的可运行对象(Runnable),当回调被执行时,它会运行。
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); // 执行 mTraversalRunnable,当下一个 VSync-app 到来的时候,会执行 TraversalRunnable 这个 Runnable
notifyRendererOfFramePending(); // 通知渲染器有一帧即将被绘制。这通常是为了确保渲染器准备好处理新的帧。
pokeDrawLockIfNeeded(); // 根据需要戳一下绘制锁
}
}
在上面的函数中执行 mTraversalRunnable,当下一个 VSync-app 到来的时候,会执行 TraversalRunnable 这个 Runnable.
scss
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal(); //在Runnable运行时调用doTravelsal,并最终调用到 performTraversals 函数
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 通过消息循环(Looper)的队列(Queue)移除之前发布的同步屏障(Sync Barrier)。
// 这个屏障是在scheduleTraversals方法中发布的,用于阻塞消息队列中同步消息的处理,直到遍历任务开始执行。现在遍历任务即将开始,所以移除这个屏障以允许其他同步消息继续被处理。
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //移除同步屏障,那么 mHandler 就可以正常处理后面的消息了
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals(); // 执行遍历任务。这个方法包含了测量、布局和绘制的实际逻辑
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
上面的代码调用 performTraversals 函数执行遍历任务。
ini
private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView; // 获取根视图(mView)并存储在局部变量host中
// 调用relayoutWindow方法,传入参数、视图可见性和是否需要处理insets(内部空间),返回重新布局的结果。
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); //内部会将经过WMS计算后的窗口尺寸给mWinFrame,经过relayoutWindow 后就 View 就可以绘制了
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,lp.privateFlags);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height,lp.privateFlags);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 调用performMeasure方法,根据新的测量规格(childWidthMeasureSpec和childHeightMeasureSpec)来测量视图的大小。
layoutRequested = true;
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
if (didLayout) { // 如果请求了布局(layoutRequested为true)且视图没有被停止(mStopped为false)或报告下一个绘制(mReportNextDraw为true),则执行布局(performLayout)
performLayout(lp, mWidth, mHeight); //调用performLayout函数
}
boolean cancelAndRedraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
if (!cancelAndRedraw) {
createSyncIfNeeded(); //设置了回调,等时机到了就会触发执行,而这个时机就是 View 绘制完成后,在 markSyncReady 触发,开始finish drawing流程
}
// 调用performDraw()尝试绘制,如果绘制失败且存在同步回调,则调用回调。
if (!performDraw() && mSyncBufferCallback != null) {
mSyncBufferCallback.onBufferReady(null);
}
if (!cancelAndRedraw) {
mReportNextDraw = false;
mSyncBufferCallback = null;
mSyncBuffer = false;
if (isInLocalSync()) {
mSurfaceSyncer.markSyncReady(mSyncId); //触发绘制完成回调,markSyncReady 方法,最终会触发 ViewRootImpl::createSyncIfNeeded 方法下的 ViewRootImpl::reportDrawFinished 来真正 finishDrawingWindow 流程。
mSyncId = UNSET_SYNC_ID;
}
}
}
在 performTraversals 函数中分别执行了 relayoutWindow,performMeasure,performLayout,performDraw 等函数。并执行 createSyncIfNeeded 函数执行完成绘制。
首先分析下 relayoutWindow 函数。
java
final IWindowSession mWindowSession; // 其初始化是在ViewRootImpl的构造函数中由传入的参数进行赋值
public final Surface mSurface = new Surface(); // 应用端这个View树的 Surface,这个mSurface是专门提供给docerview上面的canvas.
private final SurfaceControl mSurfaceControl = new SurfaceControl(); // 对应的SurfaceControl
private final ClientWindowFrames mTmpFrames = new ClientWindowFrames(); // 临时保存最新的窗口信息
final Rect mWinFrame; // frame given by window manager.当前窗口大小
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,boolean insetsPending) throws RemoteException {
mRelayoutRequested = true;
int relayoutResult = 0;
// 调用WMS的 relayoutWindow流程
relayoutResult = mWindowSession.relayout(mWindow, params,
requestedWidth, requestedHeight, viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mRelayoutBundle);
if (!useBLAST()) {
mSurface.copyFrom(mSurfaceControl);
} else {
updateBlastSurfaceIfNeeded(); // 给 mSurface 赋值,目前版本都走这
}
setFrame(mTmpFrames.frame); // 将WMS计算的窗口大小设置到当前
return relayoutResult;
}
上面的代码分别调用了 relayout、 updateBlastSurfaceIfNeeded、setFrame等函数。
通过 mWindowSession.relayout 调用WMS的 relayoutWindow 流程。 在调用 relayout 时,将 mTmpFrames 和 mSurfaceControl 作为参数传递了过去。 执行这个方法前 mSurfaceControl 只是一个没有实际内容的对象,但是经过 WMS::relayoutWindow 流程处理后,mSurfaceControl 就会真正持有一个 native 层的 Surface 句柄,有个这个 native 的 Surface 句柄,View 就可以把图像数据保存到Surface 中了。
通过调用 updateBlastSurfaceIfNeeded 函数将 mSurfaceControl 下的 surface 赋值给当前的变量 mSurface。
通过调用 setFrame 函数将WMS计算的窗口大小设置到当前,在执行了relayoutWindow 流程后 mTmpFrames 就有最新的尺寸信息了,需要赋值给真正保存窗口尺寸的变量 mWinFrame。
首先看下 ViewRootImpl::updateBlastSurfaceIfNeeded 函数。
scss
private BLASTBufferQueue mBlastBufferQueue;
void updateBlastSurfaceIfNeeded() {
// 经过system_service处理后的mSurfaceControl有值
if (!mSurfaceControl.isValid()) {
return;
}
if (mBlastBufferQueue != null && mBlastBufferQueue.isSameSurfaceControl(mSurfaceControl)) {
mBlastBufferQueue.update(mSurfaceControl,
mSurfaceSize.x, mSurfaceSize.y,
mWindowAttributes.format);
return;
}
// If the SurfaceControl has been updated, destroy and recreate the BBQ to reset the BQ and
// BBQ states.
if (mBlastBufferQueue != null) {
mBlastBufferQueue.destroy();
}
// 创建对象
mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
Surface blastSurface = mBlastBufferQueue.createSurface();
// Only call transferFrom if the surface has changed to prevent inc the generation ID and
// causing EGL resources to be recreated.
// 给当前mSurface赋值
mSurface.transferFrom(blastSurface);
}
上面的代码调用了 createSurface 函数。这个放在后面分析,先分析下 relayoutWindow 流程。
在 relayoutWindow 函数中 mWindowSession.relayout 调用WMS的 relayoutWindow 流程。其中 mWindowSession 是这样定义的,即 final IWindowSession mWindowSession; 其初始化是在ViewRootImpl的构造函数中由传入的参数进行赋值。
2, 在frameworks/base/service/core/java/com/android/server/wm/Session.java中
scala
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
final WindowManagerService mService;
@Override
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
//调用wms的 relayoutWindow 函数
int res = mService.relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, flags,
outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSyncSeqIdBundle);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
return res;
}
}
上面的代码中,在 Session::relayout 方法的参数中应用端传过来的 mSurfaceControl 变成了:outSurfaceControl,说明这是个出参会在 WindowManagerService::relayoutWindow 方法对其进行真正的赋值。 上面的函数 relayout 调用WMS的 relayoutWindow 函数。
3,在frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java中
java
final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
final WindowSurfacePlacer mWindowPlacerLocked; // 在WindowManagerService的构造函数中执行mWindowPlacerLocked = new WindowSurfacePlacer(this);进行初始化。
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,int requestedWidth, int requestedHeight, int viewVisibility, int flags,ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls, Bundle outSyncIdBundle) {
synchronized (mGlobalLock) {
// 从mWindowMap中获取WindowState
final WindowState win = windowForClientLocked(session, client, false); // 调用windowForClientLocked方法,根据传入的session和client参数获取对应的WindowState对象。如果没有找到对应的窗口,win将为null
// 获取WindowState对象对应的DisplayContent和DisplayPolicy。DisplayContent代表一个显示内容,而DisplayPolicy则包含与显示相关的策略或行为。
final DisplayContent displayContent = win.getDisplayContent();
final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
WindowStateAnimator winAnimator = win.mWinAnimator; // 获取WindowState的WindowStateAnimator对象,该对象负责窗口的动画效果
if (viewVisibility != View.GONE) {
win.setRequestedSize(requestedWidth, requestedHeight); // 把应用端请求的大小,保存到WindowState下
}
if (attrs != null) {
displayPolicy.adjustWindowParamsLw(win, attrs); // 调用adjustWindowParamsLw方法调整窗口参数。
}
win.setViewVisibility(viewVisibility); // 设置窗口可见 viewVisibility = VISIBLE
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
if (shouldRelayout) {
try {
// 创建SurfaceControl(重点)
result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); //创建Buff类型的Surface
} catch (Exception e) {
displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
ProtoLog.w(WM_ERROR,
"Exception thrown when creating surface for client %s (%s). %s",
client, win.mAttrs.getTitle(), e);
Binder.restoreCallingIdentity(origId);
return 0;
}
}
mWindowPlacerLocked.performSurfacePlacement(true /* force */); // 计算窗口大小(极其重要的方法),窗口的摆放 (View一般有变化也要执行 layout,WMS在管理窗口这边肯定也要执行layout)
if (focusMayChange) {
// 更新焦点
if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
imMayMove = false;
}
}
// 填充WMS计算好后的数据,返回应用端
win.fillClientWindowFramesAndConfiguration(outFrames, mergedConfiguration,
false /* useLatestConfig */, shouldRelayout); // 将计算好的窗口尺寸返回给应用端
}
return result;
}
上面的代码调用了 windowForClientLocked 获取Window.
java
final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) {
WindowState win = mWindowMap.get(client);
return win;
}
上面的 relayoutWindow 函数主要执行了下面三个函数,
createSurfaceControl: 创建"Buff"类型的Surface;
performSurfacePlacement: 窗口的摆放 (View一般有变化也要执行 layout,WMS在管理窗口这边肯定也要执行layout);
fillClientWindowFramesAndConfiguration :将计算好的窗口尺寸返回给应用端。 首先分析下 createSurfaceControl 函数。
createSurfaceControl函数第一个参数outSurfaceControl: WMS 创建好一个 Surface 后,还需要返回给应用端用于 View 的绘制,就是通过这个参数,由参数命名也可以知道这是一个"出参"。
第二个参数result:方法执行结果。
第三个参数win:当前窗口对应的WindowState,稍后创建Surface会挂载到这个WindowState节点之下.
第四个参数winAnimator:WindowStateAnimator对象,管理窗口状态和动画,稍后通过其内部方法创建Surface。
csharp
private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
WindowState win, WindowStateAnimator winAnimator) {
if (!win.mHasSurface) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
WindowSurfaceController surfaceController; // 创建WindowSurfaceController对象
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
surfaceController = winAnimator.createSurfaceLocked(); // 创建"Buff"类型Surface
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (surfaceController != null) {
surfaceController.getSurfaceControl(outSurfaceControl); // 出参给应用端,WMS 这边创建好后的 Surface 设置给 relayoutWindow 的参数 outSurfaceControl 。这样一来应用端就有了可以保持绘制数据的 Surface ,然后就可以执行 View 树的绘制了。
ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
} else {
// For some reason there isn't a surface. Clear the
// caller's object so they see the same state.
ProtoLog.w(WM_ERROR, "Failed to create surface control for %s", win);
outSurfaceControl.release();
}
return result;
}
上面的函数中调用 createSurfaceLocked 函数。
4,在frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java中
java
class WindowStateAnimator {
final WindowState mWin; //在构造函数 WindowStateAnimator(final WindowState win){}中被赋值。
WindowSurfaceController mSurfaceController;
int mDrawState;
WindowSurfaceController createSurfaceLocked() {
final WindowState w = mWin;
if (mSurfaceController != null) {
return mSurfaceController;
}
w.setHasSurface(false);
resetDrawState(); // 重置窗口状态 -- DRAW_PENDING
// 创建WindowSurfaceController
mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
flags, this, attrs.type);
w.setHasSurface(true);
return mSurfaceController;
}
//WindowState 状态是保存在 WindowStateAnimator 中
void resetDrawState() {
mDrawState = DRAW_PENDING; // 将 WindowState 状态设置为 DRAW_PENDING 表示等待绘制
if (mWin.mActivityRecord == null) {
return;
}
if (!mWin.mActivityRecord.isAnimating(TRANSITION)) {
mWin.mActivityRecord.clearAllDrawn();
}
}
}
上面的代码在 createSurfaceLocked 函数中创建 WindowSurfaceController 对象。
5,在frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java中
scss
class WindowSurfaceController {
SurfaceControl mSurfaceControl;
final WindowStateAnimator mAnimator;
private final WindowManagerService mService;
WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,int windowType) {
mAnimator = animator;
title = name;
mService = animator.mService;
final WindowState win = animator.mWin; // 拿到WindowState
mWindowType = windowType;
mWindowSession = win.mSession;
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
// 通过 makeSurface() 方法构建Surface 对象
final SurfaceControl.Builder b = win.makeSurface()
.setParent(win.getSurfaceControl()) // 获取到 WindowState 对象,设置为创建 Surface 的父节点
.setName(name) // 第一个参数传递的字符串name最终也会作为 Surface 的 name
.setFormat(format)
.setFlags(flags)
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
.setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
.setCallsite("WindowSurfaceController");
final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
// 高版本都为BLAST
if (useBLAST) {
b.setBLASTLayer(); // 设置为"Buff"图层
}
// 触发build
mSurfaceControl = b.build(); // build 出一个 Surface
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
void getSurfaceControl(SurfaceControl outSurfaceControl) {
// 将framework层的SurfaceControl copy给应用层传递过来的outSurfaceControl
outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
}
void setSecure(boolean isSecure) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, title);
if (mSurfaceControl == null) {
return;
}
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
mService.openSurfaceTransaction();
try {
getGlobalTransaction().setSecure(mSurfaceControl, isSecure);
final DisplayContent dc = mAnimator.mWin.mDisplayContent;
if (dc != null) {
dc.refreshImeSecureFlag(getGlobalTransaction());
}
} finally {
mService.closeSurfaceTransaction("setSecure");
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked");
}
}
}
在上面的代码中,通过 makeSurface() 方法构建Surface 对象。在Android系统中,所有图像的绘制都需要渲染到Surface上,而Surface的本质是一块渲染缓存,即GraphicBuffer。 分析到这里,surface 就完成了创建。
在WMS的 relayoutWindow 函数中,通过调用 createSurfaceControl 并进一步调用 createSurfaceLocked 以及后续等步骤创建完成 surface 后,需要设置给应用端,即在 relayoutWindow 函数中的传递的参数 outSurfaceControl. 这一步在 WindowManagerService::createSurfaceControl 放中执行 WindowSurfaceController::getSurfaceControl 时完成。
javascript
void getSurfaceControl(SurfaceControl outSurfaceControl) {
// 将framework层的SurfaceControl copy给应用层传递过来的outSurfaceControl
outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
}
这样一来应用端就有了可以保持绘制数据的 Surface ,然后就可以执行 View 树的绘制了。
上面分析了relayoutWindow函数中的 createSurfaceControl 函数,下面分析下 performSurfacePlacement 函数。
在WMS的relayoutWindow函数中调用了mWindowPlacerLocked.performSurfacePlacement(true /* force */),此时走到WindowSurfacePlacer.java中.
performSurfacePlacement 方法的调用链如下:
arduino
WindowSurfacePlacer::performSurfacePlacement
WindowSurfacePlacer::performSurfacePlacementLoop
RootWindowContainer::performSurfacePlacement
RootWindowContainer::performSurfacePlacementNoTrace
RootWindowContainer::applySurfaceChangesTransaction
DisplayContent::applySurfaceChangesTransaction
DisplayContent::performLayout
DisplayContent::performLayoutNoTrace
DisplayContent::mPerformLayout
DisplayPolicy::layoutWindowLw
WindowLayout::computeFrames -- 计算窗口大小,保存在 sTmpClientFrames中
WindowState::setFrames -- 将计算结果 sTmpClientFrames 的数据设置给窗口
6,在frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java中
ini
class WindowSurfacePlacer {
final void performSurfacePlacement(boolean force) { //在wms中调用时传递的参数是true
if (mDeferDepth > 0 && !force) {
mDeferredRequests++;
return;
}
int loopCount = 6; // 最大次数循环为6次
do {
mTraversalScheduled = false;
performSurfacePlacementLoop(); // 调用 performSurfacePlacementLoop 函数
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
mService.mRoot.mWallpaperActionPending = false;
}
}
上面的函数调用 performSurfacePlacementLoop 函数。
java
private void performSurfacePlacementLoop() {
// 执行 RootWindowContainer::performSurfacePlacement 这个代表对屏幕进行一次 layout
//在WMS中有 RootWindowContainer mRoot; // The root of the device window hierarchy.
// 对所有窗口执行布局操作
mService.mRoot.performSurfacePlacement();
mInLayout = false; // 布局完成
if (mService.mRoot.isLayoutNeeded()) {
if (++mLayoutRepeatCount < 6) {
// 布局次数小于6次,则需要再次请求布局.
requestTraversal();
} else {
Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
mLayoutRepeatCount = 0;
}
} else {
mLayoutRepeatCount = 0;
}
}
void requestTraversal() {
if (mTraversalScheduled) {
return;
}
// Set as scheduled even the request will be deferred because mDeferredRequests is also
// increased, then the end of deferring will perform the request.
mTraversalScheduled = true;
if (mDeferDepth > 0) {
mDeferredRequests++;
if (DEBUG) Slog.i(TAG, "Defer requestTraversal " + Debug.getCallers(3));
return;
}
mService.mAnimationHandler.post(mPerformSurfacePlacement); // 通过Handler触发
}
private class Traverser implements Runnable {
@Override
public void run() {
synchronized (mService.mGlobalLock) {
performSurfacePlacement();
}
}
}
private final Traverser mPerformSurfacePlacement = new Traverser();
WindowSurfacePlacer(WindowManagerService service) {
mService = service;
}
在上面的代码中 performSurfacePlacementLoop 通过语句 mService.mRoot.performSurfacePlacement()调用了 RootWindowContainer 中的 performSurfacePlacement 函数。
7,在frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java中
scala
class RootWindowContainer extends WindowContainer<DisplayContent> implements DisplayManager.DisplayListener {
void performSurfacePlacement() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
try {
performSurfacePlacementNoTrace(); //调用performSurfacePlacementNoTrace函数
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
void performSurfacePlacementNoTrace() {
if (mWmService.mFocusMayChange) { // 如果需要,则更新焦点
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(
UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
// 开启事务,在WindowContainer.java中定义有mWmService对象,即protected final WindowManagerService mWmService;
mWmService.openSurfaceTransaction(); //调用openSurfaceTransaction函数,开启Surface事务。
try {
// 处理事务(执行窗口尺寸计算,surface状态变更等操作),处理Surface事务
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
// 关闭事务,做事务提交
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (SHOW_LIGHT_TRANSACTIONS) {
Slog.i(TAG,
"<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
}
}
checkAppTransitionReady(surfacePlacer); // Activity 切换事务处理,条件满足也会将窗口状态设置为HAS_DRAW 流程
if (mWmService.mFocusMayChange) { // 再次判断是否需要处理焦点变化
mWmService.mFocusMayChange = false;
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/);
}
handleResizingWindows(); // 如果过程中size或者位置变化,则通知客户端重新relayout
// Destroy the surface of any windows that are no longer visible.
i = mWmService.mDestroySurface.size(); // 销毁不可见的窗口
}
上面函数中调用了 applySurfaceChangesTransaction 函数。
ini
private void applySurfaceChangesTransaction() {
final int count = mChildren.size(); // 正常情况就一个屏幕
for (int j = 0; j < count; ++j) { // 遍历每个屏幕执行 DisplayContent::applySurfaceChangesTransaction
final DisplayContent dc = mChildren.get(j);
dc.applySurfaceChangesTransaction();
}
}
在上面的 performSurfacePlacement 函数中调用了 performSurfacePlacementNoTrace 函数。
在函数 performSurfacePlacementNoTrace 中,先后调用了 openSurfaceTransaction、applySurfaceChangesTransaction、closeSurfaceTransaction、checkAppTransitionReady 函数等。
先分析下 openSurfaceTransaction 函数,通过 mWmService.openSurfaceTransaction()被调用,其中在WindowContainer.java中定义有mWmService对象,即protected final WindowManagerService mWmService;
8,在frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java中
csharp
void openSurfaceTransaction() {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
SurfaceControl.openTransaction();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
9,在frameworks/base/core/java/android/view/SurfaceControl.java中
typescript
public final class SurfaceControl implements Parcelable {
static GlobalTransactionWrapper sGlobalTransaction;
static long sTransactionNestCount = 0;
public static void openTransaction() { // 在WMS中被调用
synchronized (SurfaceControl.class) {
if (sGlobalTransaction == null) {
sGlobalTransaction = new GlobalTransactionWrapper();
}
synchronized(SurfaceControl.class) {
sTransactionNestCount++;
}
}
}
public static void closeTransaction() {
synchronized(SurfaceControl.class) {
if (sTransactionNestCount == 0) {
Log.e(TAG,
"Call to SurfaceControl.closeTransaction without matching openTransaction");
} else if (--sTransactionNestCount > 0) {
return;
}
sGlobalTransaction.applyGlobalTransaction(false);
}
}
private static class GlobalTransactionWrapper extends SurfaceControl.Transaction { // SurfaceControl.Transaction是一个用于控制屏幕显示内容的类,通常用于在Android系统中进行底层UI操作
void applyGlobalTransaction(boolean sync) {
applyResizedSurfaces();
notifyReparentedSurfaces();
nativeApplyTransaction(mNativeObject, sync); // 用于实际将事务应用到系统底层
}
@Override
public void apply(boolean sync) { // 被重写以抛出一个RuntimeException异常,表明全局事务必须通过closeTransaction方法来应用,而不是直接调用apply方法。
throw new RuntimeException("Global transaction must be applied from closeTransaction");
}
}
public static class Transaction implements Closeable, Parcelable { ... }
}
在分析了 openSurfaceTransaction 后,接着分析 applySurfaceChangesTransaction 函数。
在 RootWindowContainer 函数中,applySurfaceChangesTransaction 函数如下:
ini
private void applySurfaceChangesTransaction() {
final int count = mChildren.size(); // 正常情况就一个屏幕
for (int j = 0; j < count; ++j) { // 遍历每个屏幕执行 DisplayContent::applySurfaceChangesTransaction
final DisplayContent dc = mChildren.get(j);
dc.applySurfaceChangesTransaction();
}
}
上面的代码中 applySurfaceChangesTransaction 函数调用了DisplayContent类的 applySurfaceChangesTransaction 函数。
10,在frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java中
java
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {
private final DisplayPolicy mDisplayPolicy; //在DisplayContent的构造函数中定义,即mDisplayPolicy = new DisplayPolicy(mWmService, this);
private boolean mLayoutNeeded;
void applySurfaceChangesTransaction() {
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
mTmpUpdateAllDrawn.clear(); // 置空,新的执行,清除数据
// Perform a layout, if needed.
performLayout(true /* initial */, false /* updateInputWindows */); // 执行布局,该方法最终会调用performLayoutNoTrace,计算窗口的布局参数。layoutWindow 流程。
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyPostLayoutPolicy");
try {
mDisplayPolicy.beginPostLayoutPolicyLw();
// 对所有窗口执行布局策略,遍历所有窗口执行 lambda表达式。
forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
mDisplayPolicy.finishPostLayoutPolicyLw();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
try {
// 遍历所有窗口,主要是改变窗口状态设置为READY_TO_SHOW,当前逻辑不满足,不会执行最终设置
// 执行 mApplySurfaceChangesTransaction 这个 lambda表达式 ,当前分析的场景是会把目标窗口状态设置为 READY_TO_SHOW 和 HAS_DRAWN
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
// finishDrawing()流程,条件满足触发提交Surface到SurfaceFlinger
prepareSurfaces(); // Surface 操作
}
在上面的函数 applySurfaceChangesTransaction 中,调用了
void performLayout(boolean initial, boolean updateInputWindows) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
try {
performLayoutNoTrace(initial, updateInputWindows); //调用 performLayoutNoTrace 函数
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) {
return;
}
clearLayoutNeeded(); // 将mLayoutNeeded设置为flase
forAllWindows(mPerformLayout, true /* traverseTopToBottom */); // 对所有顶级窗口进行布局
forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */); // 处理子窗口的布局
}
private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
if (w.mHasSurface) {
// Take care of the window being ready to display.
final boolean committed = winAnimator.commitFinishDrawingLocked(); // 调用 commitFinishDrawingLocked 函数
}
// allDrawn相关逻辑
final ActivityRecord activity = w.mActivityRecord;
if (activity != null && activity.isVisibleRequested()) {
activity.updateLetterboxSurface(w);
// 更新绘制状态
final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
mTmpUpdateAllDrawn.add(activity); // 加入集合
}
}
};
private final Consumer<WindowState> mPerformLayout = w -> {
if (w.mLayoutAttached) { // 如果当前窗口为子窗口则直接返回
return;
}
final boolean gone = w.isGoneForLayout(); // 先判断当前窗口是否会不可见
if (!gone || !w.mHaveFrame || w.mLayoutNeeded) { // 如果窗口不是不可见的,或者窗口没有框架,或者窗口需要布局
// 调用DisplayPolicy::layoutWindowLw
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
}
};
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
if (!w.mLayoutAttached) { // 如果不是子窗口则返回
return;
}
if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame || w.mLayoutNeeded) {
getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames); // 调用 layoutWindowLw 函数
}
}
private void clearLayoutNeeded() {
if (DEBUG_LAYOUT) Slog.w(TAG_WM, "clearLayoutNeeded: callers=" + Debug.getCallers(3));
mLayoutNeeded = false;
}
boolean isLayoutNeeded() {
return mLayoutNeeded;
}
}
上面的代码中调用到了 DisplayPolicy类的 layoutWindowLw 函数。
11,在frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java中
java
public class DisplayPolicy {
private final WindowLayout mWindowLayout = new WindowLayout();
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
if (win.skipLayout()) { // 判断是否需要跳过布局
return;
}
// This window might be in the simulated environment.
// We invoke this to get the proper DisplayFrames.
displayFrames = win.getDisplayFrames(displayFrames); // 获取DisplayFrames
// 获取某个方向的窗口布局参数
final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
final Rect attachedWindowFrame = attached != null ? attached.getFrame() : null;
// If this window has different LayoutParams for rotations, we cannot trust its requested
// size. Because it might have not sent its requested size for the new rotation.
final boolean trustedSize = attrs == win.mAttrs;
// 应用端请求的宽高信息
final int requestedWidth = trustedSize ? win.mRequestedWidth : UNSPECIFIED_LENGTH;
final int requestedHeight = trustedSize ? win.mRequestedHeight : UNSPECIFIED_LENGTH;
// 调用WindowLayout.computeFrames计算窗口布局大小
mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale,
sTmpClientFrames);
// 将计算的布局参数赋值给windowFrames
win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);
}
}
上面的代码中,WindowLayout::computeFrames 计算窗口大小的,有一套计算规则。WindowState::setFrames 然后将计算好后的大小设置给 WindowState。
12, 在frameworks/base/core/java/android/view/WindowLayout.java中
typescript
public class WindowLayout {
public void computeFrames(...) {}
}
13, 在frameworks/base/services/core/java/com/android/server/wm/WindowState.java中
ini
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,InsetsControlTarget, InputTarget {
private final WindowFrames mWindowFrames = new WindowFrames();
void setFrames(ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) {
final WindowFrames windowFrames = mWindowFrames; // 获取当前窗口的框架信息实例
mTmpRect.set(windowFrames.mParentFrame); // 将父窗口框架信息复制到临时变量 mTmpRect
if (LOCAL_LAYOUT) {
windowFrames.mCompatFrame.set(clientWindowFrames.frame);
windowFrames.mFrame.set(clientWindowFrames.frame);
windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
if (hasCompatScale()) {
// The frames sent from the client need to be adjusted to the real coordinate space.
windowFrames.mFrame.scale(mGlobalScale);
windowFrames.mDisplayFrame.scale(mGlobalScale);
windowFrames.mParentFrame.scale(mGlobalScale);
}
} else {
windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
windowFrames.mFrame.set(clientWindowFrames.frame);
windowFrames.mCompatFrame.set(windowFrames.mFrame); // 设置兼容框架信息与窗口框架相同
if (hasCompatScale()) {
// Also, the scaled frame that we report to the app needs to be adjusted to be in
// its coordinate space.
windowFrames.mCompatFrame.scale(mInvGlobalScale); // 对windowFrames.mCompatFrame缩放,确保在应用坐标空间内正确展示
}
}
windowFrames.setParentFrameWasClippedByDisplayCutout(
clientWindowFrames.isParentFrameClippedByDisplayCutout);
// Calculate relative frame
windowFrames.mRelFrame.set(windowFrames.mFrame); // 计算相对框架信息
WindowContainer<?> parent = getParent(); // 将窗口框架转换为相对于父容器的坐标系
int parentLeft = 0;
int parentTop = 0;
if (mIsChildWindow) { // 如果当前窗口是子窗口,则根据其父窗口计算偏移量
parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
} else if (parent != null) { // 若当前窗口不是子窗口但有父容器,则从父容器边界获取偏移量
final Rect parentBounds = parent.getBounds();
parentLeft = parentBounds.left;
parentTop = parentBounds.top;
}
windowFrames.mRelFrame.offsetTo(windowFrames.mFrame.left - parentLeft,
windowFrames.mFrame.top - parentTop);
// 检查请求的宽高是否改变,以及父窗口框架是否有变化
if (requestedWidth != mLastRequestedWidth || requestedHeight != mLastRequestedHeight
|| !mTmpRect.equals(windowFrames.mParentFrame)) {
// 更新最后请求的宽高值,并标记内容发生变化
mLastRequestedWidth = requestedWidth;
mLastRequestedHeight = requestedHeight;
windowFrames.setContentChanged(true);
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) { // 如果窗口类型为 DOCK_DIVIDER 类型,比如分屏应用中间的分割线是这个类型
if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
mMovedByResize = true;
}
}
if (mIsWallpaper) { // 如果当前窗口是壁纸
final Rect lastFrame = windowFrames.mLastFrame;
final Rect frame = windowFrames.mFrame;
if (lastFrame.width() != frame.width() || lastFrame.height() != frame.height()) {
mDisplayContent.mWallpaperController.updateWallpaperOffset(this, false /* sync */);
}
}
updateSourceFrame(windowFrames.mFrame); // 更新源框架信息
if (LOCAL_LAYOUT) {
if (!mHaveFrame) {
// The first frame should not be considered as moved.
updateLastFrames();
}
}
if (mActivityRecord != null && !mIsChildWindow) {
mActivityRecord.layoutLetterbox(this);
}
mSurfacePlacementNeeded = true;
mHaveFrame = true;
}
}
经过 WindowState::setFrames 方法后,窗口自己就有了一个确定的尺寸了,但是绘制内容是在应用端,所以现在需要把这个窗口尺寸传递给应用端。
在WindowManagerService::relayoutWindow 方法中知道,经过 WindowSurfacePlacer::performSurfacePlacement 方法计算出窗口尺寸后, 会执行 WindowState::fillClientWindowFramesAndConfiguration 方法就尺寸信息填充到参数 outFrames 中,也就是传递给应用端。
14, 在frameworks/base/services/core/java/com/android/server/wm/WindowState.java中
typescript
void fillClientWindowFramesAndConfiguration(ClientWindowFrames outFrames,
MergedConfiguration outMergedConfiguration, boolean useLatestConfig,
boolean relayoutVisible) {
// 尺寸信息设置给应用端
outFrames.frame.set(mWindowFrames.mCompatFrame);
outFrames.displayFrame.set(mWindowFrames.mDisplayFrame);
mLastConfigReportedToClient = true; // 标记已向客户端报告过配置信息
}
上面代码中这个 outFrames 就是应用端 ViewRootImpl执行 relayout 方法触发WMS::relayoutWindow 传递的参数 mTmpFrames。 上面分析了 relayoutWindow 函数的 createSurfaceControl、performSurfacePlacement、fillClientWindowFramesAndConfiguration 等函数。
至此,relayoutWindow 的流程分析完了。