1 问题描述
从SetupWizard退出进入Launcher的过程中,SetupWizard的相关界面在退出的动画过程中短暂卡在了某个阶段,如下图所示:
2 问题分析
2.1 log分析
透过现象看本质,看log此过程中没有冻屏之类的操作,那么出现长时间卡在某个动画阶段,可能是在动画期间,相关SurfaceControl设置setPosition不够流畅导致。
在Transaction.setPosition方法中加log后,先看下正常的情况:
预期的情况是,setPosition的操作应该尽量在每一帧中都能进行,最终是界面的位置随着时间平滑的变化。
但是出现问题的时候的log为:
就没有几次设置setPosition的操作,y轴的变化也非常突兀,前几帧还算勉强在每一次Vsync的时候调用Transaction.setPosition为相关SurfaceControl设置了y轴位置,后面则出现了严重的丢帧,导致相关SurfaceControl的y轴位置一直没有得到更新。
再看具体的某一次设置position的堆栈信息:
能看到:
1)、进程号2023对应的是SystemUI,也就是说这个动画是Transition动画,并且由位于SystemUI的WMShell负责执行。
2)、动画的每一帧位置通过ValueAnimator在每一个Vsync到来的时候计算的得到,至于这个Vsync自然是由Choreographer来接收。
2.2 perfetto分析
再看perfetto的信息:
执行动画的线程2110收到了几次Vsync后,出现了一段很长的空白期,之后才继续收到了几次Vsync,完成了动画,这一点符合我们观察到的现象以及log的情况。
至于为什么动画线程很长的一段时间都没有收到Vsync,看SystemUI主线程,则是长时间在处理configChanged相关的事务,这阻塞了Choreographer接收到下一个Vsync的时候动画的执行,动画从而出现了卡顿。
所以原因出在SetupWizard的动画还在执行的时候, “com.google.android.setupwizard”和“com.ts.setupwizard.overlay”等一些App执行了一些disable App的操作,触发了全局Configuration的更新,导致SystemUI主线程去处理了configChanged相关的事务导致了阻塞。
2.3 全局Configuration的更新
最后看下为什么全局Configuration发生了更新,对比AndroidU的项目,在退出SetupWizard进入Launcher的时候,是没有发生全局Configuration的更新的。
全局Configuration更新的log为:
更新的原因为CONFIG_ASSETS_PATHS这一位发生了变化,看下这个差异是如何产生的。
这里直接放结论,起点在OverlayManagerService.mService.setEnabledExclusiveInCategory:
这里传入的packageName为“com.android.internal.systemui.navbar.gestural”,这个值定义在WindowManagerPolicyConstants中:
应该是给App用的,看起来是和导航模式中的手势相关的。
后续的调用堆栈为:
OverlayManagerService.mService.setEnabledExclusiveInCategory
-> OverlaytManagerService.updateTargetPackagesLocked
-> OverlaytManagerService.updateActivityManager -> ActivityManager.scheduleApplicationInfoChanged -> ActivityManagerService.scheduleApplicationInfoChanged -> ActivityManagerService.updateApplicationInfoLOSP -> ProcessList.updateApplicationInfoLOSP -> ActivityTaskManagerService.updateAssetConfiguration
最终是在ActivityTaskManagerService.updateAssetConfiguration中:
首先调用ActivityTaskManagerService.increaseAssetConfigurationSeq中对Configuration.assetSeq进行了自增:
然后再调用ActivityTaskManagerService.updateConfiguration对全局Configuration进行了更新。
2.4 根本原因
经过第3步的分析,看到是那次OverlayManagerService.mService.setEnabledExclusiveInCategory的调用造成了全局Configuration的更新,然后我本地屏蔽了这次调用,果然不会有“config changes”相关的log打印了, 并且看手机的现象也正常,没有卡顿的现象发生了,但是本来进入Launcher的时候是手势导航模式的,因为我屏蔽了那次调用后也变成了3按钮导航的模式,所以OverlayManagerService.mService.setEnabledExclusiveInCategory的调用是SystemUI那边为了将导航模式从3按钮模式切换为手势模式。
所以这个问题发生的根本原因是,在SetupWizard退出进入Launcher的时候:
1)、首先是SetupWizard要执行退出动画,这个动画由位于SystemUI的WMShell执行,具体为在SystemUI的主线程中当Choreographer接收到Vsync信号后在animation阶段执行动画。
2)、SystemUI将导航模式从3按钮切换为了手势,这导致了全局Configuration的更新,SystemUI主线程中需要针对这个变化进行一些耗时操作,这影响到第一步的动画的顺利执行。