1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Surface状态的状态切换流程如下:
1、在 relayoutWindow 流程创建 Surface 后在 createSurfaceLocked 方法将状态设置为 DRAW_PENDING
2、应用绘制完成会后触发 finishDrawingWindow 方法,这个方法分为以下几步:
3、layout 流程 WindowManagerService::closeSurfaceTransaction 方法里会真正将事务提交到 SurfaceFlinger 处理
所以现在更明确的当前流程的主线任务: 1、找到设置 Surface 状态为 COMMIT_DRAW_PENDING、 READY_TO_SHOW 和 HAS_DRAWN 的地方。 2、找到执行 SurfaceControl.Transaction::show 和 SurfaceControl.Transaction::apply 执行的地方就完成了。
完整流程图
本篇的主要逻辑和上篇一样,也是在一次 layout 里,layout 几户覆盖了所有的窗口逻辑,非常复杂,这里只贴出关于窗口显示逻辑流程图:
应用端处理
既然是绘制完成后的处理,触发的地方还是应用端本身,只有应用端绘制完成了才会触发逻辑。
再看一下 ViewRootImpl::setView 的调用链:
ViewRootImpl :: setView
ViewRootImpl :: requestLayout
ViewRootImpl :: scheduleTraversals
ViewRootImpl. TraversalRunnable :: run -- Vsync 相关-- scheduleTraversals
ViewRootImpl :: doTraversal
ViewRootImpl :: performTraversals
ViewRootImpl :: relayoutWindow
Session :: relayout -- 第二步:relayoutWindow
ViewRootImpl :: updateBlastSurfaceIfNeeded
Surface :: transferFrom -- 应用端Surface 赋值
ViewRootImpl :: performMeasure -- View 绘制三部曲 -- Measure
ViewRootImpl :: performLayout -- View 绘制三部曲 -- Layout
ViewRootImpl :: createSyncIfNeeded
SurfaceSyncGroup :: init
ViewRootImpl :: reportDrawFinished
Session :: finishDrawing -- 第三步:finishDrawingWindow
ViewRootImpl :: performDraw -- View 绘制三部曲 -- Draw
SurfaceSyncGroup :: markSyncReady -- 触发绘制完成回调
Session . addToDisplayAsUser -- 第一步:addWindow
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
前面分析【relayoutWindow流程】的时候已经分析过 ViewRootImpl::performTraversals 方法了,不过当前重点不一样,所以还需要再看一遍这个方法(增加了一些当前流程相关的代码)
1、后续需要介绍软绘硬绘的流程,所以可以看到硬绘的初始化逻辑也在这个方法 2、relayoutWindow 相关 3、经过第二步 relayoutWindow 后 View 就可以绘制了 4、绘制完成后就要通知 SurfaceFlinger 进行合成了,也就是本篇分析的 finishDrawing 流程
当前分析 finishDrawing 流程,首先可以看到 relayoutWindow 方法执行后,会触发3个View绘制的方法,也就是常说的 View 绘制三部曲:measure、layout、draw 。
但是这里有个奇怪的地方: “4.1 createSyncIfNeeded” 方法是触发 finishDrawingWindow 的,但是这个方法在 “3.3 performDraw”的上面。
这是因为代码的顺序不代表真正的执行顺序,这里的“4.1 createSyncIfNeeded”只是设置了“回调”,等时机到了就会触发执行,而这个时机就是 View 绘制完成后,在 “4.2 markSyncReady 触发”
这一部分的逻辑有点绕,不过目前分析的是主流程,所以这块逻辑以上的描述当黑盒理解这段的调用: View 绘制结束后就会在 4.2 出触发 4.1 内部的执行,进入触发 finishDrawingWindow 流程即可。 这部分的代码 U 做了重构,后面再单独写一篇详细解释直接的调用逻辑。
finishDrawingWindow 的触发
在 ViewRootImpl::performTraversals 方法最后会执行 SurfaceSyncGroup::markSyncReady 方法,最终会触发 ViewRootImpl::createSyncIfNeeded 方法下的 ViewRootImpl::reportDrawFinished 来真正 finishDrawingWindow 流程。
# ViewRootImpl
private SurfaceSyncGroup mActiveSurfaceSyncGroup;
boolean mReportNextDraw;
private void createSyncIfNeeded ( ) {
if ( isInWMSRequestedSync ( ) || ! mReportNextDraw) {
return ;
}
final int seqId = mSyncSeqId;
mWmsRequestSyncGroupState = WMS_SYNC_PENDING ;
mWmsRequestSyncGroup = new SurfaceSyncGroup ( "wmsSync-" + mTag, t -> {
mWmsRequestSyncGroupState = WMS_SYNC_MERGED ;
reportDrawFinished ( t, seqId) ;
} ) ;
if ( DEBUG_BLAST ) {
Log . d ( mTag, "Setup new sync=" + mWmsRequestSyncGroup. getName ( ) ) ;
}
mWmsRequestSyncGroup. add ( this , null ) ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
这个方法的重点就是在应用绘制完成后触发 finishDrawingWindow 流程,也就是触发 ViewRootImpl::reportDrawFinished 方法。
# ViewRootImpl
private void reportDrawFinished ( @Nullable Transaction t, int seqId) {
if ( DEBUG_BLAST ) {
Log . d ( mTag, "reportDrawFinished" ) ;
}
if ( Trace . isTagEnabled ( Trace . TRACE_TAG_VIEW ) ) {
Trace . instant ( Trace . TRACE_TAG_VIEW , "reportDrawFinished " + mTag + " seqId=" + seqId) ;
}
try {
mWindowSession. finishDrawing ( mWindow, t, seqId) ;
. . . . . .
} . . . . . .
. . . . . .
}
# Session
@Override
public void finishDrawing ( IWindow window,
@Nullable SurfaceControl. Transaction postDrawTransaction, int seqId) {
if ( DEBUG ) Slog . v ( TAG_WM , "IWindow finishDrawing called for " + window) ;
if ( Trace . isTagEnabled ( TRACE_TAG_WINDOW_MANAGER ) ) {
Trace . traceBegin ( TRACE_TAG_WINDOW_MANAGER , "finishDrawing: " + mPackageName) ;
}
mService. finishDrawingWindow ( this , window, postDrawTransaction, seqId) ;
Trace . traceEnd ( TRACE_TAG_WINDOW_MANAGER ) ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
这个方法的重点就是在应用绘制完成后触发 finishDrawingWindow 流程,也就是触发 ViewRootImpl::reportDrawFinished 方法。
# ViewRootImpl
private void reportDrawFinished ( @Nullable Transaction t, int seqId) {
if ( DEBUG_BLAST ) {
Log . d ( mTag, "reportDrawFinished" ) ;
}
if ( Trace . isTagEnabled ( Trace . TRACE_TAG_VIEW ) ) {
Trace . instant ( Trace . TRACE_TAG_VIEW , "reportDrawFinished " + mTag + " seqId=" + seqId) ;
}
try {
mWindowSession. finishDrawing ( mWindow, t, seqId) ;
. . . . . .
} . . . . . .
. . . . . .
}
# Session
@Override
public void finishDrawing ( IWindow window,
@Nullable SurfaceControl. Transaction postDrawTransaction, int seqId) {
if ( DEBUG ) Slog . v ( TAG_WM , "IWindow finishDrawing called for " + window) ;
if ( Trace . isTagEnabled ( TRACE_TAG_WINDOW_MANAGER ) ) {
Trace . traceBegin ( TRACE_TAG_WINDOW_MANAGER , "finishDrawing: " + mPackageName) ;
}
mService. finishDrawingWindow ( this , window, postDrawTransaction, seqId) ;
Trace . traceEnd ( TRACE_TAG_WINDOW_MANAGER ) ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
唯一做的一件事就是跨进程触发 WindowManagerService::finishDrawingWindow 。 到这里应用端的事情就处理完了,后面的流程在 system_service 进程。
system_service处理
system_service 处理主要的调用链整理如下:
WindowManagerService::finishDrawingWindow
WindowState::finishDrawing
WindowStateAnimator::finishDrawingLocked -- COMMIT_DRAW_PENDING
WindowPlacerLocked::requestTraversal -- 触发layout
Traverser::run
WindowSurfacePlacer::performSurfacePlacement
WindowSurfacePlacer::performSurfacePlacementLoop
RootWindowContainer::performSurfacePlacement -- 开始layout逻辑
RootWindowContainer::performSurfacePlacementNoTrace
WindowManagerService::openSurfaceTransaction -- 打开Surface事务
RootWindowContainer::applySurfaceChangesTransaction -- 处理Surface事务
DisplayContent::applySurfaceChangesTransaction -- 遍历每个屏幕
DisplayContent::performLayout -- relayoutWinodw 流程
DisplayContent::forAllWindows -- 每个窗口执行mApplySurfaceChangesTransaction
WindowStateAnimator::commitFinishDrawingLocked -- READY_TO_SHOW
WindowState::performShowLocked -- HAS_DRAWN
DisplayContent::prepareSurfaces -- Surface 处理
WindowContainer::prepareSurfaces -- 遍历每个孩子
WindowState::prepareSurfaces -- 忽略其他,只看窗口的实现
WindowStateAnimator::prepareSurfaceLocked
WindowSurfaceController::showRobustly
WindowSurfaceController::setShown
SurfaceControl.Transaction::show -- Surface显示
WindowManagerService::closeSurfaceTransaction -- 处理关闭Surface事务
SurfaceControl::closeTransaction
GlobalTransactionWrapper::applyGlobalTransaction
GlobalTransactionWrapper::nativeApplyTransaction -- 触发native
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
# WindowManagerService
final WindowSurfacePlacer mWindowPlacerLocked;
void finishDrawingWindow ( Session session, IWindow client,
@Nullable SurfaceControl. Transaction postDrawTransaction, int seqId) {
if ( postDrawTransaction != null ) {
postDrawTransaction. sanitize ( Binder . getCallingPid ( ) , Binder . getCallingUid ( ) ) ;
}
final long origId = Binder . clearCallingIdentity ( ) ;
try {
synchronized ( mGlobalLock) {
WindowState win = windowForClientLocked ( session, client, false ) ;
Slog . w ( TAG , "finishDrawingWindow: " + win+ " mDrawState="
+ ( win != null ? win. mWinAnimator. drawStateToString ( ) : "null" ) ) ;
if ( win != null && win. finishDrawing ( postDrawTransaction, seqId) ) {
if ( win. hasWallpaper ( ) ) {
win. getDisplayContent ( ) . pendingLayoutChanges |=
WindowManagerPolicy . FINISH_LAYOUT_REDO_WALLPAPER ;
}
win. setDisplayLayoutNeeded ( ) ;
mWindowPlacerLocked. requestTraversal ( ) ;
}
}
} finally {
Binder . restoreCallingIdentity ( origId) ;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
system_service 进程第一个处理的方法就是 WindowManagerService::finishDrawingWindow 这个方法也就做了2件事:
1、WindowState::finishDrawing 将 Surface 状态设置为 COMMIT_DRAW_PENDING 2、WindowSurfacePlacer::requestTraversal 框架层的 layout ,将状态设置为 READY_TO_SHOW ,HAS_DRAWN ,然后通知到 SurfaceFlinger
这里有上述的2个流程需要分析,首先会执行 WindowState::finishDrawing ,将WindowState状态设置为 COMMIT_DRAW_PENDING ,表示应用端已经绘制完成了,可以提交给SF了。 第一步操作完之后,就会执行 WindowSurfacePlacer::requestTraversal ,这个方法是执行一次 layout 逻辑。
在前面看窗口状态 COMMIT_DRAW_PENDING 定义的时候,google 注释提过: “会下一次 layout 的时候显示到屏幕上”,指的就是在这里触发的 layout。
在第二步 layout 的时候会遍历每个窗口,目前只关心当前分析的场景的这个窗口,在这次 layout 会做3件事:
1、将窗口状态设置为 READY_TO_SHOW 2、将窗口状态设置为 HAS_DRAWN 3、通过 SurfaceControl.Transaction 通知 SurfaceFlinger 做显示合成
下面开始在代码中梳理流程。
WindowState状态 – COMMIT_DRAW_PENDING
# WindowState
final WindowStateAnimator mWinAnimator;
boolean finishDrawing ( SurfaceControl. Transaction postDrawTransaction, int syncSeqId) {
. . . . . .
final boolean layoutNeeded =
mWinAnimator. finishDrawingLocked ( postDrawTransaction, mClientWasDrawingForSync) ;
mClientWasDrawingForSync = false ;
return ! skipLayout && ( hasSyncHandlers || layoutNeeded) ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
主要是执行了 WindowStateAnimator::finishDrawingLocked ,内部会将 WindowState 的状态设置为 COMMIT_DRAW_PENDING ,这个是非常重要的一步。
# WindowStateAnimator
boolean finishDrawingLocked ( SurfaceControl. Transaction postDrawTransaction,
boolean forceApplyNow) {
. . . . . .
if ( mDrawState == DRAW_PENDING ) {
ProtoLog . v ( WM_DEBUG_DRAW ,
"finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s" , mWin,
mSurfaceController) ;
if ( startingWindow) {
ProtoLog . v ( WM_DEBUG_STARTING_WINDOW , "Draw state now committed in %s" , mWin) ;
}
mDrawState = COMMIT_DRAW_PENDING ;
layoutNeeded = true ;
}
. . . . . .
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
这样第一步就执行完了,流程很简单,只是设置窗口状态为 COMMIT_DRAW_PENDING 。
本次layout 流程简述
上一小节只是改了状态,下一个状态是 READY_TO_SHOW ,前面看到google对它有一个注释:The surface will be displayed when the next layout is run. 也就是说在下一次 layout 会触发 Surface 的显示,所以关键流程还是在 “next layout”, 那什么是 “next layout” ? 我们知道屏幕上有任何风吹操作都会触发一次 layout 流程,主要就是执行 WindowSurfacePlacer::performSurfacePlacement 这就是 一次 layout 。
WindowPlacerLocked::requestTraversal 触发的 layout 流程就是之前 relayoutWindow 流程看到的 WindowSurfacePlacer::performSurfacePlacement 。这个流程触发的地方非常多,只是当前 finishDrawingWindow 会主动触发一次罢了。对于这种高频率触发的方法,需要留意一下,初学者知道每个主流程会走什么逻辑就好,慢慢的随着知识体系的构建,再看这个流程其实就没那么复杂了。
WindowSurfacePlacer::performSurfacePlacement 的逻辑会遍历屏幕上每一个窗口,然后让其根据最新情况做对应的处理,比如 relayoutWinodw 流程的时候就会遍历到窗口做 执行 computeFrames 计算窗口大小。
当前分析的场景自然也会遍历窗口,触发这次 layout 的目的就是让当前这个窗口的 Surface 提交到 SurfaceFlinger 。
这个流程之前看过了,所以直接从 RootWindowContainer::performSurfacePlacement 方法开始
# RootWindowContainer
void performSurfacePlacement ( ) {
Trace . traceBegin ( TRACE_TAG_WINDOW_MANAGER , "performSurfacePlacement" ) ;
try {
performSurfacePlacementNoTrace ( ) ;
} finally {
Trace . traceEnd ( TRACE_TAG_WINDOW_MANAGER ) ;
}
}
void performSurfacePlacementNoTrace ( ) {
. . . . . .
Trace . traceBegin ( TRACE_TAG_WINDOW_MANAGER , "applySurfaceChanges" ) ;
mWmService. openSurfaceTransaction ( ) ;
try {
applySurfaceChangesTransaction ( ) ;
} catch ( RuntimeException e) {
Slog . wtf ( TAG , "Unhandled exception in Window Manager" , e) ;
} finally {
mWmService. closeSurfaceTransaction ( "performLayoutAndPlaceSurfaces" ) ;
Trace . traceEnd ( TRACE_TAG_WINDOW_MANAGER ) ;
}
. . . . . .
checkAppTransitionReady ( surfacePlacer) ;
. . . . . .
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
这里就有2个主要流程,也是2个类型的事务处理:
1、applySurfaceChangesTransaction 方法,执行 Surface 事务
可以看到这行代码前后有 SurfaceTransaction 的打开和关闭,那说明这里的逻辑是会触发 Surface 操作的。
applySurfaceChangesTransaction 方法内部做很多事,比如上一篇的 layoutWindow 流程,当前分析的场景在这个方法里会执行将窗口状态设置为 READY_TO_SHOW 和 HAS_DRAWN ,并且构建一个 Surface 事务来显示当前窗口的 Surface 。
然后会在 WindowManagerService::closeSurfaceTransaction 方法中触发 SurfaceTransaction 的apply 把 Surface 操作的事务提交到 SurfaceFlinger 。
2、checkAppTransitionReady 方法,执行 App 事务。 这个方法也是很常见并且核心的,Framework 层专门定义了 AppTransition 来表示一些 APP 的事务,根据用户具体的操作执行对应的 App 事务。如果事务已经满足执行条件,则会触发对应的 AppTransition 执行,然后也有一些窗口动画的触发。 checkAppTransitionReady 触发的流程也会触发如果条件满足,也是会触发设置窗口状态为 HAS_DRAWN 逻辑的。只不过随着 google 的代码更新,目前正常启动流程,HAS_DRAWN 的设置还是在 applySurfaceChangesTransaction 触发的流程里。这个后面还会再提一下。
这个方法知道一下即可,当前流程不重点分析。
applySurfaceChangesTransaction 方法概览
# RootWindowContainer
private void applySurfaceChangesTransaction ( ) {
. . . . . .
final int count = mChildren. size ( ) ;
for ( int j = 0 ; j < count; ++ j) {
final DisplayContent dc = mChildren. get ( j) ;
dc. applySurfaceChangesTransaction ( ) ;
}
. . . . . .
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
不考虑多个屏幕的场景。
# DisplayContent
private final LinkedList < ActivityRecord > mTmpUpdateAllDrawn = new LinkedList ( ) ;
void applySurfaceChangesTransaction ( ) {
. . . . . .
mTmpUpdateAllDrawn. clear ( ) ;
. . . . . .
performLayout ( true , false ) ;
. . . . . .
Trace . traceBegin ( TRACE_TAG_WINDOW_MANAGER , "applyWindowSurfaceChanges" ) ;
try {
forAllWindows ( mApplySurfaceChangesTransaction, true ) ;
} finally {
Trace . traceEnd ( TRACE_TAG_WINDOW_MANAGER ) ;
}
prepareSurfaces ( ) ;
. . . . . .
while ( ! mTmpUpdateAllDrawn. isEmpty ( ) ) {
final ActivityRecord activity = mTmpUpdateAllDrawn. removeLast ( ) ;
activity. updateAllDrawn ( ) ;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
这里有有个3重要的流程:
1、relayoutWinodw 流程计算窗口大小(已经分析过,当前不管) 2、遍历每个窗口,执行 mApplySurfaceChangesTransaction 这个 lambda表达式 ,当前分析的场景是会把目标窗口状态设置为 READY_TO_SHOW 和 HAS_DRAWN 3、Framework 层对Surface 的操作,当前场景就是会提交一个 Surface 显示的事务
第一点在 relayoutWindow 流程分析过来,本篇分析后面2个。
这里还有一个重要的数据结构 :mTmpUpdateAllDrawn
这个集合存储的是这次 layout 执行到当前 applySurfaceChangesTransaction 方法时,哪些 ActivityRecord 需要更新 allDrawn 属性了。
ActivityRecord 下面的这个 allDrawn 变量表示当前 ActivityRecord 下面的窗口是否全部绘制。
执行方法前会先把 mTmpUpdateAllDrawn 清空,然后在方法末尾遍历是否有元素。 那这个集合的元素是在哪里添加的呢?
在每个 WindowState 执行 mApplySurfaceChangesTransaction 时,如果符合条件就会加入集合。
READY_TO_SHOW和HAS_DRAWN状态更改
状态的设置在上面说的3个重点流程的第二点,也就是这一句代码触发。
forAllWindows ( mApplySurfaceChangesTransaction, true ) ;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
这句代码会从上到下遍历每个窗口,然后执行 lambda 表达式, mApplySurfaceChangesTransaction 的定义如下:
# DisplayContent
private final Consumer < WindowState > mApplySurfaceChangesTransaction = w -> {
. . . . . .
final WindowStateAnimator winAnimator = w. mWinAnimator;
. . . . . .
if ( w. mHasSurface) {
final boolean committed = winAnimator. commitFinishDrawingLocked ( ) ;
. . . . . .
}
. . . . . .
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) ;
}
}
w. updateResizingWindowIfNeeded ( ) ;
} ;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
这里有2个逻辑需要注意:
1、主流程,将状态设置成 READY_TO_SHOW 2、allDrawn 属性逻辑
当前还是先看设置 Surface 状态的流程,allDrawn相关的后面单独梳理。
mHasSurface 在 relayoutWindow 流程创建 Surface 时设置为 true ,表示当前 WindowState 的已经有 Surface 。 然后就调用其 WindowStateAnimator::commitFinishDrawingLocked 。
# WindowStateAnimator
boolean commitFinishDrawingLocked ( ) {
. . . . . .
if ( mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW ) {
return false ;
}
ProtoLog . i ( WM_DEBUG_ANIM , "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s" ,
mSurfaceController) ;
mDrawState = READY_TO_SHOW ;
. . . . . .
if ( activity == null || activity. canShowWindows ( )
|| mWin. mAttrs. type == TYPE_APPLICATION_STARTING ) {
result = mWin. performShowLocked ( ) ;
}
return result;
. . . . . .
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
这里有4个点,其中后面2个很重要:
1、这个方法每次 layout 的时候都可能会执行过来,所以要判断当前窗口的状态是否符合条件执行后面的逻辑 2、这个日志是上层分析黑屏问题看状态状态的关键表示已经把窗口状态设置为 READY_TO_SHOW 3、窗口状态设置成 READY_TO_SHOW ,这也是当前分析的主要里程碑 4、满足3个条件之一就会执行下一步,将窗口状态设置为 HAS_DRAW
4.1 不依赖 Activity 的窗口,一般是状态栏导航栏这种系统窗口,或者应用启动的悬浮窗 4.2 ActivityRecord::canShowWindows 是否可以显示窗口。这个方法的返回值又受3个因素影响,下一小节解释。 4.3 窗口类型为 StartWindow
可以看到现在窗口的状态已经是 READY_TO_SHOW 了,至于能不能执行到下一步的条件,当前场景是 Activity 下的窗口,所以如果要进去只能满足 “activity.canShowWindows()” 这条。 先给结论,绝大部分情况下,是满足条件的,所以先继续走主线任务,然后再看这个条件的具体控制条件,已经如果这里没满足条件,那又会在什么逻辑把 Surface 状态设置成 HAS_DRAW 。
继续看刷主线任务:HAS_DRAW 。
# WindowState
boolean performShowLocked ( ) {
. . . . . .
final int drawState = mWinAnimator. mDrawState;
if ( ( drawState == HAS_DRAWN || drawState == READY_TO_SHOW ) && mActivityRecord != null ) {
if ( mAttrs. type != TYPE_APPLICATION_STARTING ) {
mActivityRecord. onFirstWindowDrawn ( this ) ;
} else {
mActivityRecord. onStartingWindowDrawn ( ) ;
}
}
if ( mWinAnimator. mDrawState != READY_TO_SHOW || ! isReadyForDisplay ( ) ) {
return false ;
}
. . . . . .
ProtoLog . v ( WM_DEBUG_ANIM , "performShowLocked: mDrawState=HAS_DRAWN in %s" , this ) ;
mWinAnimator. mDrawState = HAS_DRAWN ;
mWmService. scheduleAnimationLocked ( ) ;
. . . . . .
return true ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
这里会对当前窗口状态做检查,只有满足条件才会将状态设为 HAS_DRAWN。 然后因为这会窗口已经要显示了,可以移除 StartWindow 了。(以后会单独分析).
第一条主线任务到这里已经完成了,状态状态已经被设置为 HAS_DRAWN 了!
但是这毕竟只是代码中状态,一个类的变量而已,还没看到实际显示 Surface 的代码。具体的 Surface 显示逻辑在 prepareSurfaces 中。
SurfaceControl.Transaction::show
到这里状态状态已经是 READY_TO_SHOW 了,现在需要真正的将窗口显示事务提交到 SurfaceFlinger。
# DisplayContent
@Override
void prepareSurfaces ( ) {
Trace . traceBegin ( TRACE_TAG_WINDOW_MANAGER , "prepareSurfaces" ) ;
try {
final Transaction transaction = getPendingTransaction ( ) ;
super . prepareSurfaces ( ) ;
SurfaceControl . mergeToGlobalTransaction ( transaction) ;
} finally {
Trace . traceEnd ( TRACE_TAG_WINDOW_MANAGER ) ;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
这里的主要流程是“super.prepareSurfaces();”,不过可以看到前后先是获取到了一个事务,然后再把这个事务 merge 到全局事务,这个全局事务就是 SurfaceControl 下面的一个类,也是一个 Surface 事务。
那说明这之间的 super.prepareSurfaces() 会有 Surface 事务的处理,才需要把它 merge 到全局事务中。
当前逻辑中 super.prepareSurfaces() 内部对 Surface 的处理就是将目标创建的 Surface 显示的事务。
统一处理 GlobalTransaction 的时机就是在后面 WindowManagerService::closeSurfaceTransaction 方法处理。
DisplayContent 的父类是 DisplayArea ,不过 DisplayArea::prepareSurfaces 方法也是调用了父类 WindowContainer 的方法,所以直接看 WindowContainer::prepareSurfaces
# WindowContainer
void prepareSurfaces ( ) {
. . . . . .
for ( int i = 0 ; i < mChildren. size ( ) ; i++ ) {
mChildren. get ( i) . prepareSurfaces ( ) ;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
WindowContainer::prepareSurfaces 这个方法被很多子类重写,比如前面提到的 DisplayContent 和 DisplayArea ,另外还有像场景的 Task , ActivityRecord , WindowState 等,但是他们内部重写的逻辑也会再调用 “super.prepareSurfaces();”来遍历他的孩子,最终会调用到 DisplayContent 每个容器类,这里的调用略微有点绕,不过也不是很复杂,放慢思路理一下就好了。
其他类的重新不管,当前分析的窗口,所以直接看 WindowState 的实现。
# WindowState
@Override
void prepareSurfaces ( ) {
. . . . . .
mWinAnimator. prepareSurfaceLocked ( getSyncTransaction ( ) ) ;
super . prepareSurfaces ( ) ;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
主要是调用了 WindowStateAnimator::prepareSurfaceLocked 方法,注意参数是是获取了一个 Transaction ,说明要开始对 Surface 做操作了。
# WindowStateAnimator
WindowSurfaceController mSurfaceController;
void prepareSurfaceLocked ( SurfaceControl. Transaction t) {
. . . . . .
if ( prepared && mDrawState == HAS_DRAWN ) {
if ( mLastHidden) {
mSurfaceController. showRobustly ( t) ;
. . . . . .
}
}
. . . . . .
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
这个方法在 U 有修改,简化了流程。google的修改链接为: 谷歌修改
这里又将 Transaction 交到了 WindowSurfaceController 处理,这个类我们在 relayoutWindow 的时候创建窗口 Surface 的时候提过,Window 的 Surface 创建是在这里类控制的,那么提交的逻辑交给它也很合理。
# WindowSurfaceController
void showRobustly ( SurfaceControl. Transaction t) {
ProtoLog . i ( WM_SHOW_TRANSACTIONS , "SURFACE SHOW (performLayout): %s" , title) ;
. . . . . .
setShown ( true ) ;
t. show ( mSurfaceControl) ;
. . . . . .
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
1、这个日志很关键,表示 Framework 已经将 Surface 提交到 SurfaceFlinger 了。(严格来说需要等后面事务的apply) 2、将 mSurfaceShown 变量设置为true, 这个也是分析黑屏问题dump要看第一个关键变量,如果为 false 说明窗口并没有显示,可能是被遮挡了 3、这里看到 SurfaceControl.Transaction::show 的调用地方了, 这个 show 就说明需要把 Suface 显示, 也是 finishDrawingWindow 最终的结果。
又完成了一个主线任务 SurfaceControl.Transaction::show 现在只要找到 SurfaceControl.Transaction::apply 的执行地方,就说明 Framework 层已经通知 SurfaceFlinger 了,整个流程也就结束。
SurfaceControl.Transaction::apply–提交显示Surface事务到SurfaceFlinger
根据前面的分析,直接看 WindowManagerService::closeSurfaceTransaction 方法
# WindowManagerService
void closeSurfaceTransaction ( String where) {
try {
Trace . traceBegin ( TRACE_TAG_WINDOW_MANAGER , "closeSurfaceTransaction" ) ;
SurfaceControl . closeTransaction ( ) ;
mWindowTracing. logState ( where) ;
} finally {
Trace . traceEnd ( TRACE_TAG_WINDOW_MANAGER ) ;
}
}
# SurfaceControl
static GlobalTransactionWrapper sGlobalTransaction;
public static void closeTransaction ( ) {
synchronized ( SurfaceControl . class ) {
. . . . . .
sGlobalTransaction. applyGlobalTransaction ( false ) ;
}
}
private static class GlobalTransactionWrapper extends SurfaceControl. Transaction {
void applyGlobalTransaction ( boolean sync) {
. . . . . .
nativeApplyTransaction ( mNativeObject, sync) ;
}
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
虽然没有看到 SurfaceControl.Transaction::apply 但是这个 GlobalTransactionWrapper 也是其子类,这里调用 native 方法也就是 apply 了。
finishDrawingWindow流程总结
当前流程在 layout 逻辑的处理流程图大概如下: 这里处理完后在 WindowManagerService::closeSurfaceTransaction 方法把这一次 layout 中对各个窗口的 Surface 操作统一提交到 Surface 处理,这样做有一个好处就是如果逐个提交不仅仅浪费资源,而且还能存在不同步的情况导致显示异常 ,把这一次 layout 所有的 Surface 提交统一放在一个 Transaction 里做提交,这样能确保显示的正常。 这样一来 finishDrawingWindow 流程的两条主线:状态,SurfaceTransaction 的 apply 就都执行完了。
data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/baiduwaimai/article/details/142305363","extend1":"pc","ab":"new"}">>
评论记录:
回复评论: