google 分屏 横屏模式 按 home 键界面错乱故障分析(一)

你确定你了解分屏的整个流程?

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

代码也是有情感,你若爱她,就调试她吧。

代码阅读,请到此处http://androidxref.com 查看原生代码

分享此文便是对代码GG的支持,也是爱的表达方式,所以让爱来的猛烈些吧。

之前分析文章列表:

Android 关机对话框概率没有阴影故障分析

android recent key长按事件弹起触发最近列表故障分析

google 分屏 popup无法显示故障分析

问题描述

[Dialer&&MMS]进入分屏后在横屏模式按home键界面错乱

操作步骤

1.进入拨号盘
2.长按recent进入分屏,按home回主界面
3.点击MMs进入短信,转到横屏模式
4按home键,故障发生

环境描述

 android7.0.1

 屏幕分辨率 720*1280

 手机:eng版本

故障效果(看状态栏,出现两条黑线)

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

分析

00

套路,使用hierarchyviewer 工具,去找下出错的内容属于谁,属于哪个类。(为什么频繁使用这个呢?快速便捷的定位,能不高呼)

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

展看DockedStackDivider,查看界面信息:

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这里看到定位信息真多,我们看到这里三个View都是自定义的,这就让我们轻而易举的找到了地方

于是我们快马加鞭,来项目里面查找下DividerView

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这里我们看到了代码属于packages\systemui下面,于是我们可以得出一个结论,分屏的线条是在SystemUI进程,于是乎,我们是可以调试SystemUI的,我们先不去调试,直接看代码分析。

01

缓下,我们先要去看DividerView.java是个什么内容。按照之前的讲法,我们来看看(只说重点了,详细的去之前文章阅读了)

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

非常普通了,就是个简单的自定义View而已
《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》
这个onFinishInflate方法就是初始化View的地方,于是我们看到一些View,我们此处关注mBackground 和mMinimizedShadow(为什么,因为我们出错的就是这两个显示出来了)

这里我高亮了mHandle,这个是拖动分割线的响应View哦。
(内心激动的人,可以先去看本文件里面的onTouch函数即可)

我们先处理此问题,关于分屏流程,后面展开。

本文搜索mBackground,核心关注它变为隐藏的时候。我们看到
《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》
只找关键的了,定义的和设置变量的一些乱七八糟的就没截图了。
我们看下 mBackground.setScaleY(MINIMIZE_DOCK_SCALE);的代码上下文
然后我们看到完整代码:(有两处,一个是有动画,一个没有而已)
《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

 MINIMIZE_DOCK_SCALE的定义为:《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》
看到这里,有隐藏view的逻辑,setScaleX(MINIMIZE_DOCK_SCALE=0)便会将此View隐藏。
我们看下这里的逻辑,
如果不是最小化minimized(就是显示的了),那就走resetBackground方法。
这里我们看到,系统将mBackground设置了锚点居中,缩放还原为1,设置mMinimizedShadow隐藏
如果mDockSide == WindowManager.DOCKED_TOP 设置PivotY为0(锚点为0,作为缩放的原点)然后将Y方向缩为0
如果mDockSide == WindowManager.DOCKED_LEFT或者DOCKED_RIGHT ,设置锚点,设置X缩放为0

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

(此处代码有些搞笑,都是隐藏,你这时候缩放X Y方向为0有区别吗?并且在上面resetBackground是直接将XY的缩放都回到1),人家还原的时候都不管你之前到底缩放了哪个方位,你自己缩放判断个鬼。so。。。出错就在这里了,你说你搞笑不?

这里有个很有意思的套路:
mMinimizedShadow.setAlpha(minimized ? 1f : 0f);
看这里是不是反的? 如果最小化,显示这个,如果不是最小化,隐藏。
这里他做这个是干嘛的呢?

其实google这个在最小化的时候显示mMinimizedShadow,按照这个名字,它会是个shadow(让你知道这个是分屏了,有个阴影效果),如果显示分屏的时候,它就隐藏了。( 它就是想在你分屏隐藏的时候,在状态栏上做个阴影,让你知道你处在分屏模式下而已)

我们看下除了DOCKED_TOP ,此枚举都有哪几个值:

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

看这个的目的,我们可以看出上面的代码,是否忽略掉了一些状态,于是我们继续来看。

02

我们再看下我们的代码

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

会发现我们的else是不是没有全部的选择,少了DOCKED_BOTTOM 和 DOCKED_INVALID,于是我们假如这里的mDockSide==这两个的其中一个,会发现什么问题呢?

mBackground 没有隐藏哦
mMinimizedShadow 是设置了显示,但是我们再去它的类去瞅瞅吧。

mMinimizedShadow  类的方法里面有:(onLayout 和onDraw都是自定义view的关键实现,还有一个是onMeasure,此处没有复写而已 )

我们看下这几个方法:

onLayout

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

updatePaint方法:

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

onDraw

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》
看到了没
这里它也没管这个值DOCKED_INVALID(DOCKED_BOTTOM),于是用了默认的颜色,而默认画出来是黑色,你说这就没意思了吧。

忽略DOCKED_BOTTOM这个枚举我们可以解释,因为当前系统设计不会放置在下面的,于是DOCKED_BOTTOM值可以忽略,所以我们看到,此处有一个 DOCKED_INVALID状态,会导致在隐藏分割线的时候,没有处理代码,引起分割线显示在上面。而系统自以为所有手机都跟它一样,配置很高,but现实是还有低配机子的啦,于是此状态会产生,引出此问题。

于是,我们看完了代码,从逻辑上分析出来是这个原因,那么事实觉得这个情况会发生吗? 我们补充log,查看下这里的错误时候的值,发现,此处为-1( DOCKED_INVALID),于是得出结论了。

到这里,此问题就算完结了,但是,但是,我要讲故障修复,就没必要这么繁琐了,因此,我们还要继续深入,去看看代码。我要去讲下分屏这条线索,追个路径出来。

03

搜索DividerView,我们继续来深入

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

我们打开Divider.java,发现了很多内容。

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

首先,我们看下它继承了谁?SystemUI,这个要干嘛呢?我们搜索Divider,通过筛选(只在SystemUI包下,为什么,之前已经说过,这个类在这个包下,别的应用引用的机会基本为0)
我们看到如下内容:(筛检过)

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

然后我们在SystemUIApplication.java 里面稍微停留下:看到 《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》 startServicesIfNeeded方法

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这里遍历了我们上面的mServices里面的所有元素,有我们的Divider.java(看这里都转为了SystemUI类处理了,所以我们Divider要继承SystemUI,没毛病)
主要走里面的start方法 和onBootCompleted方法

我们先不回到Divider的start方法,我们再继续深入看下,看startServicesIfNeeded如何调用起来的。

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

我们跟了下KeyguardService.java  发现不对路,放弃掉。
我们忽略自己本身的方法,于是来到SystemUIService.java里面

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

发现onCreate里面调用了startServicesIfNeeded方法,于是我们继续看,搜索SystemUIService

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

我们忽略注释和xml,以及本身的文件,于是我们看到了SystemServer.java(熟悉不?系统server创建的地方),我们看看去

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》 《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

看到了不?系统创建完server,会到mActivityManagerService的systemReady,这里面启动了systemUI,然后调用了一个SystemUIService,这个在创建自己的时候,调用了SystemUIApplication里面的startServicesIfNeeded,完成了systemui的组件创建和初始化,而这里,也有我们分屏的Divider

04

逛完了系统创建systemui的过程,我们再次回来,慢慢来看我们的分隔线Divider.java

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

从上面的流程来看,我们需要调用关注它的start方法,我们看下:
这里我高亮了几个内容:
DividerWindowManager ,分隔线管理者。(等会细看)
update 这个主要更新我们的参数,主要为移除Divider,然后添加(依据当前屏幕的横竖屏处理),判断是否为最小化,是的话就要想办法隐藏了。
mDockDividerVisibilityListener 这个类DockDividerVisibilityListener,我们看下:

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这里我们发现,它的继承为:IDockedStackListener.Stub,于是乎,它是跨进程调用
这里我们使用ssp.registerDockedStackListener(mDockDividerVisibilityListener);将这个监听注册到系统里面去(后面分析它)
mForcedResizableController 暂时先放过,后面分析。

05

我们说完了大概,然后我们回来看下DividerWindowManager这个类

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

没有继承,只有方法,于是我们看方法
add(关键),直接通过windowmanager给系统加入了一个View。高亮一个信息TYPE_DOCK_DIVIDER(专门给它量身定做的类型,是不是很开心,我们又找到了关键字)
《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

remove,移除这个View。

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

setSlippery 设置是否在滑动中,中间的那个线是可拖拽的。

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

setTouchable是否可点击。

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

我们回过头看看Divider里面的update方法:

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

(看下这里的removeDivider 和addDivider)
removeDivider
《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这里mWindowManager就是DividerWindowManager,我们不用说了吧。

addDivider

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

加载布局,设置大小,设置宽高,加载到windowmanager里面去。

06

总结上面的内容:

systemui里面有个Divider,里面管理着分割线的布局,有个监测系统的服务端(mDockDividerVisibilityListener),系统要不要显示,通过这条线回调回来,再通知界面显示与否即可。

这里Divider还有个方法:onConfigurationChanged,当系统属性发生改变时候,会通过这条线路发送回来(这里会做简单的事情,先移除view,然后依据当前的屏幕方向,新建view,然后根据是否要显示,默认是显示的。如果不需要显示,则隐藏掉view–用了缩放XY大小为0和Alpha来做的)

07

总结看完,继续奔波,我们先向系统层迈进,于是我们仔细来看下DockDividerVisibilityListener

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

看看它有哪些实现
onDividerVisibilityChanged显示隐藏的通知
onDockedStackExistsChanged 存在与否的通知
onDockedStackMinimizedChanged最小化的通知(我们当前在这个里面,因为我们没有退出分屏,只是进入了主界面,分割线会最小化)
onAdjustedForImeChanged 当有输入法的时候,调整大小和位置的通知
onDockSideChanged 当dock的位置调整的时候,主要就是dock在左边还是右边,这种信息。

我们先不去看这里面每个方法的具体实现,我们找下它们在哪里被调用的(我们以onDockedStackMinimizedChanged来作为搜索依据)

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

自己本身可以忽略
NavigationBarView里面是个空实现,忽略
我们看到了DockedStackDividerController.java

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

继续搜索

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

ActivityManagerInternal.java,注释忽略

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》
ActivityManagerService.java  关键地方,主要完成dockstack的动作

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

DockedStackDividerController.java
《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》
registerDockedStackListener 注册的地方(我们之前在Divider的start里面,有注册这个的动作)

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

setMinimizedDockedStack 

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这是个内部方法,我们说过,内部方法外部不能直接调用,所以我们要找这个在本文里哪里调用了

animateForMinimizedDockedStack 内部,我们还是要找谁用它了

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》WindowManagerService.java 是个case,然后调用了

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

mAmInternal(ActivityManagerInternal)的通知即可。mAmInternal是谁呢?

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

几经周转,在ActivityManagerService.java里面有

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这么多,发现线索主要在

setMinimizedDockedStack方法

(DockedStackDividerController.java)

animateForMinimizedDockedStack 方法                      (DockedStackDividerController.java)

和ActivityManagerService.java里面

于是我们线索收敛了。我们继续向下走

08

我们不做太多扩散,我们从setMinimizedDockedStack切入下
在DockedStackDividerController.java里面找setMinimizedDockedStack

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

(突然发现,这里面代码错综复杂,如此分析下去,我要陷入其中,系统还是调用太复杂,要讲清千丝万缕,不能如此细致入微去讲了,汗,于是我们先从单向切入看下)我们看这个是退出与否的状态切换


《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》我们先继续看看notifyDockedStackExistsChanged的调用地方,我现在不去用编辑器来只是简单搜搜了(如此下去,没有尽头,调用地方太多,于是我们换个思路),开始调试system_server
我们关注下WindowManagerService里面的 代码

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这个是多用户时候,需要判断当前用户是否处在分屏模式下,我们暂时可以忽略。

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

按照注释,是attachstack的时候有通知 (DOCKED_STACK_ID ,特殊栈id,主要就是标志谁在分屏的那个栈上)所以这两个函数是个关键点,我们可以下断点,去跟踪代码流程。
detachStackLocked 退出也有调用notifyDockedStackExistsChanged,于是乎我们 上断点,调试下

attachstack 方法的栈信息为:

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

detachStackLocked 的栈信息为:

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这里关注的栈方法为:

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

通过两个栈信息,我们便可以得到关键的两个东西:启动分屏的栈,关闭分屏的栈,这两个在分屏模式如此重要的方法,已经被我们拦到,其余的不是迎刃而解吗?

我们继续跟踪detachStackLocked流程,会发现我们的notifyDockedStackMinimizedChanged 方法被触发了。

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

这个是我们退出分屏的时候,发送回来的消息,于是我们需要看下这个是谁调用的

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

看看看,又是windowAnimator,又是animateLocked方法,我们清晰的看到了

doFrame(如呼吸一般)如期的出现在眼前。 我们看下animateLocked方法里面的一行

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

来到了DockedStackDividerController的animate方法

《google 分屏 横屏模式 按 home 键界面错乱故障分析(一)》

看此处,如果mAnimatingForMinimizedDockedStack为真,则走入我们的最小化方法了。

先消化下,下一章节再见。

下一章节,继续分析分屏,主要讲解分屏的启动过程。

如果有收获,赞赏鼓励下作者。

更多内容,关注微信公众号:代码GG之家 。

加微信 code_gg_boy  进入代码GG交流群

    原文作者:ActivityManagerService
    原文地址: https://juejin.im/entry/58c1486eda2f60715874bfd0
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞