1 问题描述
偶现问题,用户打开夸克、抖音后,在界面上划动无响应,但是没有ANR。回到Launcher后再次打开夸克/抖音,发现App的界面发生了变化,但是仍然是划不动的。
2 log初分析
复现问题附近的log为:
用户打开夸克的时间点为“00:27:25”左右,然后是持续的划动无响应,最后在“00:27:36”最后退出了夸克。
现在基本上已经没有冻屏的操作了,从log中也没有看到可能的痕迹,并且根据用户的描述,在夸克界面上划动无响应,切换到别的界面后再切回夸克,看到夸克的界面是有更新的,说明应该是在绘制或者合成的地方卡住了。
由于默认的log有限,无法定位到具体的问题,因此需要添加log。
3 第一次添加log
由于不确定是哪个阶段出了问题,因此先针对Input流程和measure、layout以及draw流程添加了log,后面测试再次复现问题后,查看log发现问题出在draw流程(其实从问题描述上也能看出来,从App切换到Launcher再切换回App,界面发生了变化,说明输入事件应该是被App正确接收到了)。
第一帧正常:
从第二帧开始异常:
异常的点在ViewRootImpl.performDraw:
这里就是问题的直接原因了,明明是亮屏状态,但是ViewRootImpl.mAttachInfo.mDisplayState保存的值却是Display.STATE_OFF,导致代码误判断此时处于灭屏状态,因此不会继续走绘制流程。
至于为什么第一帧有没问题,而从第二帧开始就出问题了,因为第一帧的时候,ViewRootImpl.mReportNextDraw为true,所以没有提前return,可以继续走draw流程。而从第二帧开始,ViewRootImpl.mReportNextDraw就变成了false,导致代码提前返回。
搜索代码,AttachInfo.mDisplayState赋值的地方只有四处:
1、View.dispatchMovedToDisplay
2、ViewRootImpl.onMovedToDisplay
3、ViewRootImpl.setView
4、ViewRootImpl.mDisplayListener.onDisplayChanged
前两处是跟移动到新的Display相关的,可以排除,第3处是窗口首次添加,也可以排除,那么就只有第4处是Display状态改变后,更新AttachInfo.mDisplayState的地方。
另外根据本地调试也的确如此,当发生亮灭屏时,触发的是ViewRootImpl.mDisplayListener.onDisplayChanged:
DisplayManagerGlobal.DisplayManagerCallback.onDisplayEvent
-> DisplayManagerGlobal.handleDisplayEvent
-> DisplayManagerGlobal.DisplayListenerDelegate.sendDisplayEvent
最终的调用起点在系统进程的DisplayManagerService。
由于之前对DisplayManagerService了解比较少,所以还不清楚问题出在哪儿,这次加的log不太够,需要继续在DisplayManagerService处加log,然后让测试帮忙去复现问题。
但是同时别的同事也合入了几个google的patch,导致后续问题复现不到了,这个问题一度没有了下文,当时觉得还挺可惜,花了挺长的时间去分析,结果后面复现不到了。
4 第二次添加log
虽然之前的问题不了了之了,但是后面别的项目又继续复现了该问题,希望又被点燃了…先在DisplayManagerService中添加一些log,然后继续让测试同事帮忙复现,最终成功复现到了问题。
以下是当时的log分析:
4.1 11-12 22:25:14 - 第一次启动抖音
:
这个是后续发生问题的抖音界面第一次启动的时间,此时是正常的。
中间又多次启动抖音以及亮灭屏,但是都没有问题,所以我们跳过这些阶段。
直接看发生问题前的那次启动抖音的情况。
4.2 11-13 10:33:39 - 再次启动抖音
ViewRootImpl保存的DisplayState已经是2了,即Display.STATE_ON,这次绘制没有问题。
4.3 11-13 10:53:42 - 灭屏
此次DisplayManagerService向SplashActivity对应进程“17885”进行了通知。
最终抖音对应进程“17885”也打印了相应的onDisplayChanged的log。
4.4 11-13 10:55:07 - 亮屏
DisplayManagerService没有通知抖音对应进程“17885”,有以下log打印:
11-13 10:55:07.