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

问题描述 [Dialer]it will appear different behaviors after long press the menu to exit split screen

操作步骤 1.Enter dialer 2.there is no recent items 3.long press the recent key to enter the split screen,then long press the recent again to exit the split,it will appear different behavior----KO Note:没有其他进程运行,进入dialer,长按menu键成功切换到多窗体,然后再长按menu键退出多窗体,有时会进入到recent列表 EXPECTED BEHAVIOUR: long press the recent key,it will exit the split screen and enter dialer full screen

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

分析

逐步剖析代码,探寻根本原因

01

套路,使用hierarchyviewer 工具,去找下虚拟按键三个按钮的元素信息,属于哪个类。

展看NavigationBar,查看界面信息:

找到细枝末节,发现每个元素,属于KeyButtonView.java

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

这里我们看到了代码属于packages\systemui下面,于是我们可以得出一个结论,三个虚拟按键的处理属于SystemUI进程,于是乎,我们调试SystemUI。

02

缓下,我们先要去看KeyButtonView.java是个什么内容。

主要看几个信息:

继承谁

构造方法

public方法

Override方法

此处为:继承自ImageView(于是我们知道它是类似ImageView的啦,那么我们基本可以从ImageView猜出KeyButtonView至少能做什么,至少ImageView一般都是要显示图的啦)基础的长按,短按消息都是可以支持的了。

构造方法

从此处得到信息:由自定义style,有关键的三个属性,我们此处关心前两个,看注释

keyCode 代表了键值,我们知道虚拟按键就是在模拟实体按键,因此也是需要键值。具体键值对应表,查看KeyEvent.java即可。

keyRepeat 是否可以多次响应,其实就是是否支持长按了。

构造方法一般主要是看下构造的时候都做了哪些事情,可以从变量初始化上做个了解。

public方法

主要就是看下它对外给出了哪些方法,可以让我们了解它能对外做出什么反应。

playSoundEffect 播放声音效果,主要就是按键时有个反馈

sendEvent 发送事件,这个关键方法,模拟了发送按键的动作

主要按键参数为,什么时间,按下还是弹起,什么按键,是否长按

onTouchEvent

关键的方法,重写了父类的方法。

我们需要慢慢来看这里的逻辑:

主要ontouch的事件为

MotionEvent.ACTION_DOWN

MotionEvent.ACTION_MOVE

MotionEvent.ACTION_CANCEL

MotionEvent.ACTION_UP

事件经过如下:

按下(ACTION_DOWN)后开始计时,如果一段时间ViewConfiguration.getLongPressTimeout()后,没有释放(ACTION_UP)

说明用户想长按,于是我们的postDelayed扔出了一个Runnable来进行长按处理。如果在ViewConfiguration.getLongPressTimeout()之内,用户释放(ACTION_UP)了,那就是个短按事件了。

我们先看下短按事件,在按下的时候,判断是否有键值mCode!=0,如果是,模拟按键,发送一个按键。

sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);

在我们释放的时候,doIt判断是否按下了,并且不是长按。

首先判断mCode!=0时,如果doIt是,代表我们需要处理这个短按弹起事件,如果短按弹起,sendEvent(KeyEvent.ACTION_UP, 0);

否则,发送sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);

如果mCode==0时,如果doIt为真(代表我们是个短按弹起事件),调用了onclick方法。

我们再来看长按事件,在按下的时候,判断是否有键值mCode!=0,如果是,模拟按键,发送一个按键。

sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime); 同时启动了一个postDelay消息,如果时间到,系统会调用

mCheckLongPress运行起来,(我们要看长按,此时可以假设我们按下一直没有释放,时间到后触发此Runnable)

我们一睹mCheckLongPress芳容

如果按下了,长按了,调用performLongClick 触发onlongclick消息,否则如果支持长按,我们发送长按事件。

弹起时,在我们释放的时候,doIt判断是否按下了,并且不是长按。

首先判断mCode!=0时,如果doIt否,代表我们此时是长按弹起,如果

mCode有值,我们触发sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);(我们的问题点就在这里)

Override方法

主要是复写父类方法,需要自己处理一些变量,对于系统改变,做出正确的相应。

performAccessibilityActionInternal 这个是辅助功能,模拟按键动作。

03

好了,扯了这么多,我们再次回到出发点。我们看了这个KeyButtonView.java源码,再次需要回到使用它的地方,通过我们之前的搜索,以及视图信息,可以找到使用它的xml布局文件

我们可以看到它们的父类View是NavigationBarInflaterView.java,此处展开去讲,就有些说不完了。

我们说下NavigationBarInflaterView.java主要干了什么,主要完成虚拟按键view的加载,构造,初始化。

简单走一遍流程

它就是初始化view,做了各统一管理

我们加快些步伐,可以从视图看到最终为NavigationBarView.java-->NavigationBarInflaterView.java

于是我们再看下这个NavigationBarView.java

这里发现它在初始化里面,给back,home,recent按键附上了图片。

除此之外,没发现别的。于是我们需要项目去找,谁使用了NavigationBarView.java

主要在找什么呢?

我删掉了一部分,这里想强调的是,我们知道是哪个View了,想找它在哪里用,代码肯定是会有的,xml里面一般都会是在某个布局文件里面了。我们使用此方法,可以不用断点的方式,就可以将我们的目标缩进,能够追到真正需要的代码。

如上,我们被带到了PhoneStatusBar.java

04

神奇的PhoneStatusBar.java到来,我们看到了接近真相的地方(引用虚拟按键的布局地方)

好了,我们不进行更详细的追踪了,打住在这里。

inflateNavigationBarView 将NavigationBarView布局加载进来,于是我们可以去本文件搜索,哪里用了mNavigationBarView变量

满世界都是,我们找啊找,看到了这里:

到达目的地,我们找到了是如何将虚拟按键加入布局的了,也知道怎么显示出来了(mWindowManager.addView),如此我们明白了,虚拟按键通过NavigationBarView.java包裹了一个NavigationBarInflaterView.java (navigation_bar.xml内容)实现,在PhoneStatusBar.java里面进行了加载,加载在了sysetmUI进程。

05

绕了一圈,主要就是带着走走流程,讲下一些在大脑的过程。我们现在回到我们的问题。

我们问题描述为:进入dialer,长按menu键成功切换到多窗体,然后再长按menu键退出多窗体,有时会进入到recent列表

主要关注点:view的设置回调函数,此处为KeyButtonView.java ,还有此处特殊,因为是自定义view(KeyButtonView),于是复写的方法onTouchEvent也变成了关注点。

于是我们梳理如下:

在phonestatusBar.java里面,给recentsButton注册了点击事件,touch事件(此处可忽略这个,主要是为了预加载最近列表做的准备动作,于本文分析无关)

长按事件。

这里有人有疑问了,我们看到的是个ButtonDispatcher 啊,哪里来的KeyButtonView,于是我们进入实现看下

NavigationBarView的构造里面,它的子view是NavigationBarInflaterView,它里面完成了初始化动作,具体为

inflateButton方法里面的

继续跟进:

所有流程扫过,我们再次整理

recent虚拟按键注册了

点击事件

touch事件(可以忽略)

长按事件

本身(keyButtonView.java)的onTouchEvent方法。

于是我们需要看,此时如果我们长按recent按键,弹起来的时候流程啦。

我们再次回到keyButtonView.java的onTouchEvent方法,来看

此处的信息为:

mCode=187(KEY_SERACH 也就是这里定义出来的recent的键值,具体在KeyEvent.java可以找到)

doIt 不为真

于是我们走到了流程

sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);

此时系统发出了一个按键弹起事件,此事件导致了进入了recent列表。(有时不会的原因是长按在触发分屏的时候就去释放,此时消息会被冲掉,因为随后的分屏逻辑会覆盖掉之前的recent列表)

此时我们要找的便是,sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);到底做了什么。

通过之前的讲解,我们知道sendEvent 目标就是想向系统扔出一个按键(此时为keycode= 187 action=ACTION_UP flag=KeyEvent.FLAG_CANCELED)

06

我们此时,进入一个系统处理key值的地方

PhoneWindowManager.java 的 interceptKeyBeforeDispatching方法

我们recent键值为KEYCODE_APP_SWITCH

我们看下case项:

这里为:down为按键按下 repeatCount 为是否是长按 keyguardOn 为是否锁屏下。这里我们看到,在我们弹起的时候,触发了toggleRecentApps 进入最近列表,引出此故障。

如果要修复,有两个思路:

A在此处对于弹起事件,加入一个处理,就是判断下是否为KeyEvent.FLAG_CANCELED,如果是,不做响应即可。

B在recent 的 KeyButtonView.java里面,up有段代码,判断了是否有mCode,如果有,则会在长按后在释放按键时触发send,而如果没有mCode,则没有动作,因此我们可以修改此处的code值。

结论:

系统设计时,对于是否有code的虚拟按键,定义了两组逻辑,引出此问题。

修复

我们采用B方案处理。

我们来到NavigationBarInflaterView.java 里面(加载了各个button虚拟按键),对于RECENT,使用的layout为:recent_apps.xml

修改此处的

systemui:keyCode="187" 为

systemui:keyCode="0"

验证OK,收工。

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

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

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android干货园

Base封装(二)-- 封装属于自己的Library(UI篇)

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/73...

1281
来自专栏熊二哥

Vue快速入门

终于进入国内当前最火的前端框架Vue.js的学习了,最近周边的哥们也开始用该框架做线上项目,闲暇之余,做个快速的了解,重在基础部分。 ? 基础概念 目前在国...

4588
来自专栏hightopo

快速开发基于 HTML5 网络拓扑图应用1

2752
来自专栏快乐八哥

Angular企业级开发(3)-Angular MVC实现

1.MVC介绍 Model-View-Controller 在20世纪80年代为程序语言Smalltalk发明的一种软件架构。MVC模式的目的是实现一种动态的...

2249
来自专栏移动开发之家

利用百度地图实现支付宝“到位”功能(地图模式)

算了,还是聊今天我们要说的,支付宝的“到位”功能。新版支付宝上方第四个tab,传闻丈母娘足不出户,同城招女婿的神兵利器。反正上面那些一块钱看看花、逛逛街、喝喝酒...

2381
来自专栏Lambda

Idea 常用快捷键

———–自动代码——– 常用的有fori/sout/psvm+Tab即可生成循环、System.out、main方法等boilerplate样板代码 例...

2186
来自专栏Python爬虫实战

Python爬虫之四:今日头条街拍美图

运行平台: Windows Python版本: Python3.6 IDE: Sublime Text 其他工具: Chrome浏览器

891
来自专栏繁花云

11.14学习总结

解决方案:mysqli_set_charset($conn,utf8)

830
来自专栏Albert陈凯

2018-11-07 史上最全Vim快捷键键位图 -- 入门到进阶史上最全Vim快捷键键位图 -- 入门到进阶

本文所有键位图虽然都不是博主原创,但是所有资源均为博主亲自收集整理。如需全文转载,希望尊重原作者和博主的劳动成果,保留原文链接。 本文所有图片为了方便读者使用均...

821
来自专栏云飞学编程

Python学习,还在用正则或者bs4做爬虫吗?来试试css选择器吧

之前写的一些爬虫都是用的正则、bs4、xpath做为解析库来实现,如果你对web有所涉及,并且比较喜欢css选择器,那么就有一个更适合的解析库—— PyQuer...

1012

扫码关注云+社区

领取腾讯云代金券