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

问题描述 [Message][Input method]Display is wrong when message at split mode. 分屏模式下短信界面显示不正确

操作步骤 1.打开message然后退出 2.打开一个app如Call,然后长按recent键进入分屏模式 3.让message在分屏模式中处于底部,然后在message中编辑一些字符 4.长按这些字符串,不能显示出"CUT COPY SHARE"这3项 --KO

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

首先

按照bug描述,此时无法弹出pop框(cut copy...),我们第一步方向,去跟踪代码,追到此框弹出路径。(根据log分析, 使用调试工具Eclipse打断,跟踪流程),得到一条调用栈信息:

Editor.java

--->startSelectionActionModeInternal

--->mTextView.startActionMode

DecorView.java

--->startActionModeForChild

--->startActionMode

--->createFloatingActionMode

--->FloatingActionMode

FloatingActionMode.java

--->repositionToolbar

--->updateViewLocationInWindow

--->repositionToolbar

结论

确定位置FloatingActionMode.java 的 repositionToolbar方法上,此处 if (isContentRectWithinBounds()) 判断上:

分析

前面的结论,写的非常粗糙,只是给出了大致结果,没有给出如何处理此问题的,如下我们慢慢展开。

01

使用hierarchyviewer 工具,我们全屏下操作出来copy 对话框,去看它的视图信息。

展看后我们看到了:

看下cut copy这个框中的元素,发现最终类为:FloatingActionMode.java,因此直接定位在了这个类。

大致去阅读下这个类,同时工程下搜索下这个类,很快可以看到一条轨迹。在轨迹路途加入断点,快速定位流程,搜索内容如下:

我们可以排除StatusBarWindowView.java(因为我们不属于这里),我们忽略本身的FloatingActionMode.java ,来到了DecorView.java位置,(一看代码,有戏)

于是我们上断点,定位出来流程。

02

通过跟踪,对比全屏和分屏下出错的流程,发现问题点在于updateToolbarVisibility 函数的调用上,全屏下会调用这个show,而出错的在分屏下的底部时,没有调用。

于是我们继续定位,去找谁调用了这个函数。再次筛选,我们需要调用到show方法上。寻着这个路径,我们看到了本文件里面的调用关系:mMovingOff调用了updateToolbarVisibility方法

继续找mMovingOff的调用位置,可以得到如下

两个条件,最终确定是第一个条件出现问题,出现点为:

这里代码的意思为:

mContentRectOnScreen 弹出框在全屏的显示区域

mScreenRect 全屏区域 (错误点在这里)

mViewRectOnScreen view在全屏的显示区域

此段代码做了校验,判断popup框是否在屏幕外,如果在,就不要画了(画了你也看不见)

错误是因为:此段代码判断结论为,popup不在可见范围,不用画。(我擦,有没搞错,我在编辑框上选个内容,需要复制,粘贴,怎么会不在可见范围,哭晕..)

然而错误的原因你会泪奔的,原因是

mContext.getResources().getDisplayMetrics().heightPixels 的值为558,而popup的位置是579(系统判断579>558 ,所以在屏幕外?OMG,我觉得是在开玩笑),郁闷的是我们手机屏幕是720*1280的,(579<1280,应该要画的)。于是我们愤怒转移到了getDisplayMetrics().heightPixels方法,此方法取出来的不是屏幕高,是不是有些崩溃,那么为什么不是呢?

03

让我们停止怀疑人生,继续来追踪

mContext.getResources().getDisplayMetrics().heightPixels 为什么会给错呢?

最终

我们发现:

系统getDisplayMetrics().heightPixels此方法给出的是当前task的高度值,并非屏幕的高度值。

由于之前我们没有分屏机制,所以task就是全屏的,这两个值一致,没有问题。当分屏产生时,此值大小则不是屏幕的高度了。这个属于分屏开发暴露的问题。

至于为什么分屏在上面时候,pop能弹出来,留个疑问给大家。

我们现在来查询heightPixels从何处来。此过程太过漫长,喝杯茶,容我慢慢道来。

mContext.getResources() 找到这个方法实现的地方,通过断点,找到此处的mContext在ContextImpl.java里面

getResources() 返回mResources ,于是我们要去找mResources的赋值地方,发现在ContextImpl的构造里面:

于是在ContextImpl的构造函数设置断点,发现确实此处传递的overrideConfiguration参数中有我们需要的错误值。

因此可以断定,此处之前已经有问题啦。

通过栈信息,我们可以看到,此时传递的路径为WindowManagerService.java的 handle里面的ADD_STARTING case里面,调用了addStartingWindow 方法

于是乎,我们看到了wtoken.mTask.mOverrideConfig 这个值决定了此处的overrideConfig。

如何去找哪里触发的这个case,我们搜索ADD_STARTING

在调用地方设置断点,如此可以找到调用路径。

于是我们发现setAppStartingWindow 里面调用了,我们向上去找,发现了此处的wtoken里面的值已经出错(此处为279,densityDpi值为2,和之前的558对应上了),于是我们的方向便是去找这个值从哪里来的。

通过栈信息,我们找到了ActivityStarter.java 里面的 startActivityUnchecked方法,看到了此处的mStartActivity.task值已经出错,于是我们需要在此处确认此值的来源。

于是我们向上跟踪,发现修改地方在setTaskFromReuseOrCreateNewTask函数里面,继续跟进去看:

结论已经出现:

由于我们的task的isResizeable()返回true,使得方法进入task.updateOverrideConfiguration(mBounds);此处task处在分屏时候,此时task的大小需要使用activity的边界值做覆盖,覆盖之后,使得我们最终调用mContext.getResources().getDisplayMetrics().heightPixels拿到的是task的高,并非屏幕的高。

等等,我们好像发现了什么?

这里我们再去细分析,发现此处逻辑没有问题,当前task如果是isResizeable的,那么我们是需要覆盖这个值的,因此这里值没有问题,此处逻辑追踪的只是想确定错误值的来源。通过看完,发现此值本身没有疑问,是task的大小,没有问题。

我们错了?why??

那我们再返回到我们定位的起点,此处判断错误,引起没有去显示popup框

mContentRectOnScreen 弹出框在全屏的显示区域

mScreenRect 全屏区域 (错误点在这里)

mViewRectOnScreen view在全屏的显示区域

mScreenRect 系统期望拿到的是屏幕大小,(task默认不分屏下是等于屏幕大小)而此处因为分屏了,task的大小不等于屏幕大小了。

而此段代码认为mContext.getResources().getDisplayMetrics().heightPixels拿到还是系统屏幕大小,导致出错的。

结论 mContext.getResources().getDisplayMetrics().heightPixels 真正意义上是task的大小,在不分屏下,和屏幕大小相等(当然这里屏幕大小不是真正物理屏幕大小,因为还有状态栏和虚拟按键不在task的范围内,具体就不扩展了)

于是我们的修改思路便是,需要找到此处可以拿到屏幕大小的方法,解决此问题。

修复 FloatingActionMode.java 里面,修改isContentRectWithinBounds的方法实现,具体修改为:

修改前:

修改后:

编译版本,验证OK。

原文发布于微信公众号 - 代码GG之家(code_gg_home)

原文发表时间:2017-02-18

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

【golang】调优工具 pprof

Golang 提供了 pprof 包(runtime/pprof)用于输出运行时的 profiling 数据,这些数据可以被 pprof 工具(或者 go to...

1323
来自专栏ml

ijg库解码超大型jpeg图片

1. ijg库解码超大型jpeg图片(>100M)的时候,如何避免内存溢出。        采用边解码边压缩的策略,每次解码一行或者若干行图片数据,然后对于这些...

4018
来自专栏游戏杂谈

TexturePacker压缩png的命令

压缩png效果最好的当然是TinyPNG这种神器了,不过一般情况下TexturePacker压缩出来的也基本上能达到效果。

1732
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(72)-微信公众平台开发-消息处理

前言 Senparc.Weixin.MP SDK提供了MessageHandler消息处理类 在作者的Wiki中也详细说明了如何定义这个类,下面我们来演示,...

2189
来自专栏LeoXu的博客

启动 mini-web 报错 java.lang.ClassNotFoundException...

在学习Springside的实例mini-web的时候遇到了Tomcat报错:

942
来自专栏Crossin的编程教室

【编程课堂】词云 wordcloud

本周为大家带来炫酷好玩的 wordcloud 词云构造库。 使用 wordcloud 可以做出这样的图片: ? 还可以做出这样的: ? 接下来,我们来学习如何制...

39811
来自专栏逍遥剑客的游戏开发

MPQ 文件系统完成

1884
来自专栏生信宝典

R语言学习 - 热图简化

热图绘制 - pheatmap 绘制热图除了使用ggplot2,还可以有其它的包或函数,比如pheatmap::pheatmap (pheatmap包中的phe...

4479
来自专栏Hongten

在 Excel 工作簿中定义决策表(Oracle Policy Modeling-Define decision tables in Excel workbooks)

要在 Excel 中编写规则,您只需在表中编写规则,并使用 Oracle Policy Modeling 样式标识单元格中的信息类型,

993
来自专栏JadePeng的技术博客

Latex 公式在线可视化编辑器

寻觅 最近的一个demo需要用到Latex公式在线编辑器,从搜索引擎一般会得到类似http://latex.codecogs.com/eqneditor/edi...

8256

扫码关注云+社区