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

android 同时被 3 个专栏收录
233 篇文章 3 订阅
10 篇文章 0 订阅

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

以玩的心态,做着感兴趣的事情而已,别无其他杂念。

android recent key长按事件弹起触发最近列表故障分析
google 分屏 popup无法显示故障分析

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

问题描述
[MMS]系统对话框弹出,背景为白色,没有阴影

 
操作步骤

1.进入短信
2.进入编辑界面
3.随便输入内容,选择返回,弹出对话框。此时长按Power键,弹出关机提示框–KO

效果如下(注意,界面和代码有可能有差异)

环境描述

 android7.0.1
 屏幕分辨率 720*1280
 手机:eng版本

 
01
如上套路,使用hierarchyviewer 工具(为什么使用它呢,因为可以快速的定位元素,让自己定位搜索更加快速,准确)
我们可以找到如下信息:


这里我们关注点为(没有什么特殊的View,就是很普通的系统View,于是我们找可以特殊的此界面的信息,此处为id)
这里我们看到有个id=reboot_layout
于是我们进入项目,全局搜索此字串,在xml过滤下,可以看到

找到了GlobalActions.java 文件,此时我们转换到android原生代码(由于已知原因,手头项目代码不做截图,我们转向android原生代码进行讲解)
按照我们之前教的方法,需要看构造函数,复写方法,继承对象,以及公共方法。此处就不多讲了,需要慢慢体会。(前一篇分析有讲到,可以阅读)
android recent key长按事件弹起触发最近列表故障分析

02
我们阅读代码,可以看到一个方法,清晰明了。showDialog,看名字也知道它是显示对话框了,于是我们仔细瞧瞧。


这里我们要注意的是setTitle就是我们看到的在试图工具中显示的名字。(其实这个GlobalActions也是可以作为关键字搜索的)

我们看下showDialog里面的createDialog,这个可以找到我们需要的代码。

这里截图的意思为:数组添加一个PowerAction对象,后面创建一个GlobalActionsDialog,然后注册了点击事件,长按事件。

我们看下PowerAction类,可以看到它实现了onLongPress方法(大家可以试着长按此按键,会提示进入安全模式)
短按onPress进入关机。

03
瞎转悠一圈,我们看了下关机对话框怎么创建的,Item的响应都有了。我们现在描述下问题点。(原生没问题,我们看有问题的代码)

这段代码意思为:给Dialog加入一个阴影,阴影深度0.7(0-1),具体可以移步
https://www.oschina.net/question/54100_36513了解(FLAG_DIM_BEHIND)的用法
04
回到我们的问题,有时会出现阴影,有时没有,那么难道我们这个属性有问题,有时没成功吗?于是我们搜索下FLAG_DIM_BEHIND,去看看都有哪里在用它,看看大概。

这里我们讲下怎么过滤内容。
txt xml文件不是我们的目标,我们需要java里面查找。.h也可以忽略掉,一般只是注释 定义之类的
DimLayerController.java 我们看到了目录在wm里,关键,需要关注。
FakeBackgroundService.java 看代码,是个特定响应方法,可忽略。(因为我们不会触发)
GlobalActions.java 我们使用Dialog的地方,可以忽略(我们要找系统如何处理这个属性)
PhoneWindow.java 需要关注,因为我们Dialog也是一个Phonewindow,所以肯定需要看。
PhoneWindowManager.java 需要看,因为系统核心处理位置,看了下是启动显示对话框的,肯定无关。
SoftInputWindow.java 输入法里面的,忽略
VolumeDialog 也是具体使用地方,忽略
Window.java
WindowManager.java
WindowState.java  关键地方,是此属性逻辑处理地方。

所以我们整理下来,需要看的为

DimLayerController.java 
PhoneWindow.java
Window.java
WindowManager.java
WindowState.java 

我们一看DimLayerController.java  开头的注释

便发现它是个核心代码,先放下,把其他的看下

PhoneWindow.java 只是赋值,没啥其他逻辑,忽略了。
Window.java 看下,是个方法,赋值的,可以过了。setDimAmount方法

WindowManager.java 很多,但都是常量值和注释而已,没逻辑代码。

WindowState.java ,有代码了,需要关注。

05
我们阅读了一遍,排除了一堆文件之后,茫茫之中剩余了两个文件,所以关注点集中在这里了。(WindowState.java 和DimLayerController.java)

我们先看WindowState.java

applyDimLayerIfNeeded 看名字就是在处理这个,这里清晰的标明了意义,此处我们可以看到,核心调进了
mDimLayerController.applyDimBehind 和 mDimLayerController.applyDimAbove 方法

看,我们来到了最终目的地。DimLayerController类
看看谁使用了它

DisplayContent.java 这个在用
不做过多扩展,主要说下这里mDimLayerController 系统是设计成可以重用,就是同一时刻其实只需要一个阴影,因此它是一个层,会动态变更到对应的窗体上,然后我们传下此参数给到surfaceflinger里面,它会最终绘制出来,最终体现在屏幕上便是一个对话框下有个阴影效果。
详细的View流程,参考:
http://www.2cto.com/kf/201407/317148.html

updateDimLayer 函数方法,主要完成更新状态,创建阴影层,同时判断是否需要将此阴影给到对应的DimLayerState(每个窗口在WMS里面对应的数据结构类型 TASK 和STACK,都实现了DimLayerState方法)这里我不能细细去讲它(当然我也说不清,呵呵,工程代码层次大多,浩瀚星空我没太大精力在里面肆意畅游
私有方法,可以快速先跳过,因为有人用它,必然要使用public方法。
startDimmingIfNeeded 我们之前遇见过,就是applyDim 会调用它。
我们看过一遍它的代码,发现它只是赋值,于是我们还是需要去找谁在真正用这个。
于是乎,我们又看到一个方法

真相就在这里了。

让我们唱歌庆祝下,喝个茶继续来看:

这段代码,在遍历mState列表,根据状态,算出最前面的一个需要使用阴影的窗口,然后结束。(我们去测试,调试以及打印log,发现此处会出现
mState有时短信对话框在前,有时系统关机在前,如果谁在前面,按照这里逻辑,从后向前计算,会出现前面的将后面的覆盖掉,最终系统判断为前面的需要阴影。)

而错误的时候,恰恰是短信在前,覆盖了系统关机对话框的提示阴影,使得界面显示上,虽然系统对话框在前,但是阴影却放在了短信的后面,导致问题产生

看到这里的差异,我们继续看下mState,看下为什么引起这个的呢?

我们看它的类别,便可以知晓,有兴趣的可以细细去看这个ArrayMap.java的实现,就会发现问题。
我们简单看下ArrayMap这里的put方法

发现是键值对,于是乎我们找找我们使用地方DimLayerController.java 里面的getOrCreateDimLayerState,我们可以看到,这个是用的对象地址,作为key传入的。

系统创建(关机对话框和短信对话框的时候)new出来的地址谁大谁小,是不确定的。而此处却用了for有序的去判断了哪个窗体需要阴影,引出问题。

我们在给出一个线索,主要是跟踪到底谁在最终触发了调用操作了阴影层。我们可以通过这条线路,追溯到系统绘制的起始位置的。
其实我扯这么多,真正处理问题的时候,不会看这么多,主要就是大概的看下附近代码逻辑,能够准确定位出来修改地方的实际含义即可,并不需要完全掌握所有流程。

我们通过这条栈,继续往下看看:
我们看到了一个构造函数,关键点Choreographer.FrameCallback()  
此调用就像是人的呼吸一样,会一直调用(可继续搜索,此处不演示了)。因此系统会频繁的走入animateLocked,这个可以理解成我们的每一帧(系统在每一帧判断下当前窗体动画,当前view动画,计算位置,然后画出来,这样我们就看到了动画效果)
我们找下WindowAnimator的使用地方,通过搜索,可以定位在 WindowManagerService.java里面

关于Choreographer 我们后续有空再讲了,当前就说到这里。

我们现在回到起点,看看我们的问题:

系统对话框弹出,背景为白色,没有阴影

我们看了一圈流程,问题点最终定位在系统处理dimlayout(阴影到底属于哪个task)的时候,计算的依据竟然是按照栈的new地址(很搞笑的行为)
按照我们的理解,你肯定是需要判断当前windowstate哪个窗体在最上面的,要将此阴影给到最上面的窗体才算OK。

此问题最终只给出方案,未做实质修改

此问题修改建议:

1 关机对话框不要使用FLAG_DIM_BEHIND,自定义的时候指定一个全屏view来实现。

2 修改DimLayerController里面对于FLAG_DIM_BEHIND的处理流程,需要改成依据windowMap列表来实现(修改太大,但是是最正确的解决方案)

3 有个比较取巧的修改思路。将DimLayerController里面 animateDimLayers的for循环修改下。原因是此时我们的关机对话框和mms的对话框非常特殊。关机对话框的taskstack会是0(homestack),而mms的肯定不是。而其他应用的taskstack肯定也不是。而出现taskstack的情况,只有在home界面或者是系统级别的对话框,由于home界面不会存在和其他应用的对话框重叠概念,所有和其他应用重叠的,只会是系统级别的对话框啦。因此此处可以判断是否有taskstack等于0的需要阴影,如果有,就不要靠后进行了,直接给到它即可。

好了,此处无验证,收工。

如果有收获,赞赏鼓励下作者。
更多内容,关注微信公众号:代码GG之家。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值