1、问题描述:
横屏下相机分屏切换到小窗口,点击预览界面跳动。
2、根本原因:
T 架构 升级导致。
在 FWK 中 ,ViewRootImpl 类中,有一个 relayoutWindow 方法,T 上 是 通过rotation的角度进行计算(如上图),然后赋给 transformHint 属性,后面流程跑的时候拿的就是计算之后的值,而在 S 及之前版本 上都是直接通过 JNI 获取到的该值(如下图)
3、问题分析:
1、先把 代码运行的流程搞清楚
在此问题情景下,小窗的时候,点击预览界面,除了 AF/AE 触发的流程的之外,设置windowMode,以及剩下的比较相关的WindowConfiguration 的设置—— ScreenBrightness(window.setAttributes(layoutParams))
2、log 分析
在发生异常的时候,我们可以看到 点击之后,首先进行了AF/AE 的操作,然后 不知道为什么触发了 surfaceChanged 动作。
3、具体分析:
T 上 新增问题。本身流程导致?版本升级导致?
首先 AF/AE 的流程 并不会涉及到 Surface 显示相关,可以排除 这个方向,暂时放弃windowMode的设置流程,因为竖屏的时候 是不会的。先排查WindowConfiguration 信息改变引起的 window 重构。
先查一查 Window 类的 setAttributes 方法
在这个方法中,把window信息进行了分发,而 window 中的 mCallback 接口,实际在Activity 类中进行了实现,因此此 window 信息的改变 就传递到了 activity 中,然后再经过 WindowManager,最终又传递到了 ViewRootImpl 类中,按我的理解是 ViewRootImpl 是处理 视图树以及事件的 大管家,所以要传递回去进行处理。
最终 走到了 ViewRootImpl 类中的 setLayoutParams 方法。在这个方法中,针对此问题的话,它是标记了 window 信息的改变,然后 发出绘制请求。
省略掉与此问题 不相关的 流程绘制显示的流程,有兴趣的话,可以自行研究研究,最终会 走到 ViewRootImpl 类中的 performTraversals 方法
在performTraversals方法中,我们看到触发了 relayoutWindow 的操作,此处也与log 中 对应了起来。先是触发了relayoutWindow 方法,然后 又触发了 updateSurafce 方法。
在relayoutWindow 方法中,经过版本比对,hint字段通过 rotation 值进行计算更新,而在 Android 32及之前的 版本中,都是通过 JNI 获取,并不会计算更新,会不会是因为这个呢?先画上问号。
然后 咋们 再反着推,log中既然显示触发了 SurfaceView 的 surfaceChanged 回调,那么肯定是 走到了SurfaceView 类中的 updateSurface 方法,而且 还要至少一个条件为 true,从 log 中 我们可以看到 hint 字段是 true,其他字段都是 false,所以 我们的 关注点 就转移到了 hint 字段上。
再结合 上面的 mark 点,因此 得出结论,hint计算更新出错,而出错是和DisplayRotation挂钩的,而 DisplayRotation 是 activity 中Resource 资源获取,通过添加log 发现:
确实如此,activity 的displayRotation与父级Context得到的DisplayRotation不一致。
为什么不一致呢?
因为 CameraActivity 类中 复写了 Context 类的 getResource 方法,导致与 父类得到的 是不一样的 实例对象。所以当前activity无论怎么旋转,得到的 displayRotation 的值 永远 为 0.
故而 竖屏没有问题,在横屏状态下hint 计算出错,导致异常发生。
4、解决方法
通过 反射,强制 设置 DisplayRotation为 super 类的值。