一句话需求:用户在app内使用三指长按屏幕2s,app执行操作A;
功能很简单,牛逼的同学估计几分钟能处理好,特意说这个事,是自己给自己挖坑,特意分享下心里历程,大神们可以直接无视。。
(文章较长,可以直接到结尾看重点把~)
一:挖坑
需求关键字:三指,长按,2s,执行(这不是需求原话么)
当时的处理逻辑是这样的:判断用户手指个数,如果是3个,设置一个runnable 2S后执行,执行时再判断用户手指个数,如果还是3个,执行操作A,over;(懂的大神已经笑了~)
当时信心满满的上线后,发现使用的uv\pv都非常高,非常不合理,因为app上没有功能引导,用户也没有三指的行为习惯,所以肯定是出bug了;
跟进这问题2个步骤,1)看用户反馈,结果上千条反馈,都没有一条相关反馈,那是否可能是打点问题? 2)查看统计代码,也没发现异常;(一脸懵逼)
问题到底在哪里?新增了用户的场景统计,发现部分用户在色情网站会连续出现打点,本地尝试没发现问题,就让其他同学帮忙用用,结果发现问题了!!(一个人的力量是有限的,理解也是有限的~)
原来用户是有多指(3指及以上)进行缩放的习惯,如果用户是使用三指进行缩放,而且还是一直在屏幕不停缩放(即没有手指离开屏幕),就会出现问题了,因为代码只判断触发前后的手指个数,中招了!(事后跟产品沟通,这种用户可能是平时有用iPad的习惯。。而色情网站是用户在看图片或者漫画,为了看得更清晰,所以需要缩放,就出现问题了,但是还是没想懂如此明显问题,居然没用户反馈?)
噗,多明显的设计问题啊~!
二:二遇坑
重现后问题就好办了,逻辑重写,处理event事件,触发逻辑不变:
1)当用户手指个数为3个且每只手指按下时间差不大于50ms(防止用户是一只手指点击后再放第二只手指,这种场景是无效的,因此设定个两个手指的时间差),就会设置一个runnable 2S后执行;
2)其他情况,长按后,产生up事件(有手指抬起)、move事件大于一定距离(认为用户在滑动)、大于3只手指,这些场景都会把执行removeRunnable操作,则把runnable取消,避免触发操作a逻辑;
这样算是把门槛调高了,上线后,误触发的占比大幅下降,但是,仍然有百分之一的用户误触发,虽然人均pv大部分为1!!对比原来动不动误触发,的确是有优化效果的,但是,百分之一的uv也是很困扰~
经过好几轮灰度,最终发现一个问题,问题用户的event常规统计数对不上,那说明有其他没有统计到事件在一直执行,最终发现是cancel事件!!!
再次懵逼,cancel事件理论上是不会触发,至少自己本地用几台机器都没出现。
android文档的说明很简短,想看明白很难。国外一网页说的还比较详细,写在这里分享给大家:
“原文是这样的:
You receive this when a parent takes possession of the motion, for example when the user has dragged enough across a list view or scroll view that it will start scrolling instead of letting you press the buttons inside of it.
意思是这样的:
当你的手指(或者其它)移动屏幕的时候会触发这个事件,比如当你的手指在屏幕上拖动一个listView或者一个ScrollView而不是去按上面的按钮时会触发这个事件。”
当时懵逼,我们的场景没有这种行为,为什么还会有cancel事件,肯定有其他原因,最终终于找到了:
“当控件收到前驱事件(什么叫前驱事件?一个从DOWN一直到UP的所有事件组合称为完整的手势,中间的任意一次事件对于下一个事件而言就是它的前驱事件)之后,后面的事件如果被父控件拦截,那么当前控件就会收到一个CANCEL事件,并且把这个事件会传递给它的子事件”,划重点:被父控件拦截!!
最后经过多次验证,的确是被拦截了,但是,是被rom拦截了!通过统计数据,发现oppo r9及以上机器很容易出现该问题,人均pv高达几十次,后来找来机器验证,发现问题了:原来这些手机系统上自带了一个三指上/下滑动进行手机截屏的功能,而原理就是监听event事件,如果发现手指个数等于3,操作系统层直接返回cancel事件,而客户端没有针对cancel事件做处理,因此导致逻辑继续跑,意味着用户执行系统三指截图功能时,顺带把app的这个三指功能也触发了~(这个问题已经跟oppo的研发沟通,的确如此,而且一加、小米、魅族等新机器都有此功能,如果手动在系统设置把系统的三指功能关闭,则app的三指功能恢复正常,再次验证这个假设);
虽然发现了问题,但是开心不起来,因为后来发现不同厂商虽然都有这功能,但是实现的方案不一样,这里不细聊,后面权衡后,决定对oppo的手机做兼容处理;(系统逻辑是三指长按后滑动,我们的逻辑是三指长按2s,其实还有区别,但是被系统强奸了。。)
处理方案讨论了好久,也好了好多大神沟通,纷纷表示没办法,毕竟是操作系统返回的,你能怎么办?话说这么说,但是后来还是针对cancel事件做了特殊的处理;
通过调试发现,一旦触发这种cancel事件,oppo系统会一直返回cancel事件(原始是触发move,系统把所有事件触发都返回cancel,对于系统而言,move是很高频的且很短,而如果真的触发了系统的截屏,每次截屏耗时基本在200ms以上,针对这一特性做手脚)
1)在收到cancel事件时,判断之前触发的runnable是否已经存在;如果存在,则手动把runnable取消;
2)如果每次cancel事件大于200ms,则认为触发了系统的截屏,则把这个事件相加保存起来,原因是排除move事件的影响;并且执行次数+1;
3)当执行次数少于15次且时长大于2s,则认为满足条件,此时判断是否为3只手指且没明显滑动操作,如果是,则立即触发执行操作a;(设置次数是为了提高门槛,不然随着次数的增加,肯定会有达到2s的情况,这样就没意义了)
代码上线了,虽然不是百分百杜绝误触发,但是占比再次下降,基本达到万分之一,这事后来就不折腾了~
如果有更好的解决办法,欢迎一起讨论,这个方案并非合理方案,只是简单处理,而整个功能虽然一句话,但是从编码到最后发现,用了一个月多点,而不停灰度收集统计是时间的大头~
总结:
1)研发的概要设计文档流程还是需要的,别以为只是一句话需求就不做了;容易陷入想当然;
2)event容易出现兼容性问题,厂商有可能做了定制处理,这块要切记;
全文终,谢谢大家~