专栏首页代码GG之家Android 关机对话框概率没有阴影故障分析

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

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之家。

本文分享自微信公众号 - 代码GG之家(code_gg_home),作者:陆晓明

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-03-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Scoops android app多主题架构(五)

    原理分析 核心代码位置 https://github.com/52inc/Scoops/tree/master/scoops/src/main/java/com...

    用户1263308
  • Scoops android app多主题架构(二)

    简要说明 通过注释方式,完成多主题的效果更换。可以减少一些代码,使得代码更清晰一些。 ? 使用说明 1 首先写一个注释类 ? 2 在MainApp 初始化的时候...

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

    问题描述 [Dialer]it will appear different behaviors after long press the menu to ex...

    用户1263308
  • C#之美—抽象类与接口孽缘

    在很久很久以前我们的祖先将我们大自然所有能动的物体都定义成“动物”。但是后来在动物的群体当中,有一类动物进化的非常快,它们的智商明显高出其他动物,它们就是“人类...

    王清培
  • 使用DotNetBar制作漂亮的WinFrom界面,自定义AgileEAS.NET SOA平台WinClient主界面

    一、前言     AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开...

    魏琼东
  • Silverlight Telerik控件学习:带CheckBox复选框的树形TreeView控件

    在web开发中,带checkbox的tree是一个很有用的东东,比如权限选择、分类管理,如果不用sl,单纯用js+css实现是很复杂的,有了SL之后,就变得很轻...

    菩提树下的杨过
  • .NET框架设计(常被忽视的框架设计技巧)

    阅读目录: 1.开篇介绍 2.元数据缓存池模式(在运行时构造元数据缓存池) 2.1.元数据设计模式(抽象出对数据的描述数据) 2.2.借助Dynamic来改变...

    王清培
  • 17.Swift学习之类

    YungFan
  • 《coredump问题原理探究》Linux x86版3.8节栈布局之栈溢出coredump例子

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/detai...

    血狼
  • tf.summary.*函数

    在TensorFlow中,最常用的可视化方法有三种途径,分别为TensorFlow与OpenCv的混合编程、利用Matpltlib进行可视化、利用TensorF...

    周小董

扫码关注云+社区

领取腾讯云代金券