阅读 5892

记一次悬浮窗的上线以及坑点总结

       刚刚发了一个大版本,终于有时间来总结一下技术点了,今天就来总结一下上个版本用到的悬浮窗吧,类似于微信公众号文章里的效果,可以拖动可以点击,拖动以后根据拖动的位置显示不同的悬浮窗图片,具备左右自动吸顶效果,当前app不在前台时会自动隐藏。目前已经顺利上线,很多问题已经解决,线上并没有出现大的问题。今天我想把自己这次使用悬浮窗的方法以及其中的坑点告诉大家,希望能对大家有所帮助,碰到问题也可以一起讨论,欢迎小伙伴们一起进步,come on快上车。


       最开始接到悬浮窗的需求也是有点虚的,因为之前没有做过这方面,只是听说过这个比较坑,等做完才发现这个是真的坑。废话少说,直接进入主题,首先从我调研这个需求说起吧。遇到没做过的需求,习惯性地去往github上搜索悬浮窗相关的第三方库,希望能站在巨人的肩膀上。不出所以然,我找到了一个第三方git库:github.com/yhaolpz/Flo…,我下载了demo跑了一遍,发现还不错,支持滑动事件和点击事件,可以设置大小,可以设置悬浮窗的显隐。心里乐滋滋地准备往自己项目里搬运,可是刚想搬的时候发现就不对劲了,我们项目是在需要的时候再申请权限,而不是打开app就申请权限并显示。于是我一通乱改,由于对这一套不了解,改完发现越改越乱bug越多,心态已经崩了。这个时候突然灵光一闪,先去issue里找找看有没有人碰到同样的问题,果然是船到桥头自然直,还真让我找到了。有个大神碰到相同的问题并且已经解决了,fork了代码并且进行了修改,这个git库是:github.com/baneyue/Flo…,在此非常感谢前辈们的付出,站在巨人的肩膀上的感觉就是如此奇妙,自己也要多出一些技术博客分享给别人,因为每个人做的技术需求都不大相同,应用场景不一样也会导致遇到的Bug不经相同,要取长补短,哈哈。

       安卓的悬浮窗还是比较坑的,最坑的是权限,其次没有成熟的官方库来做这个事情,导致很多开发者自己去做或多或少地都有不同的问题,再次悬浮窗是window,本身谷歌提供的api就很难使用,比如只能add不能replace,并且多次add还会导致crash,这种奇葩的为难开发者的设计真是毁三观啊。吐槽完了活儿还是得自己干,这里先给大家介绍一个第三方悬浮窗代码的具体实现吧,代码也比较简单,可能我一贴出来大家一下就能看懂了。

悬浮窗关键代码解读

1.如何申请权限

首先静态申请一下

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />复制代码

然后为了兼容高API,需要进行动态权限申请,这里需要根据API版本以及厂商进行区分了。如果版本大于26使用TYPE_APPLICATION_OVERLAY的方式即可,否则使用TYPE_PHONE。其次一般6.0以前是不需要动态申请悬浮窗权限的,但小米是个特例,而且小米不同版本申请的方式可能还不一样,在6.0以上申请方式和其他手机一样,但是6.0以下就不同了


小米6.0以下申请权限

小米6.0以下申请权限和小米MIUI版本有关,如下图,需要对miui版本进行区分。具体的代码是我直接用的上面那个第三方库里的,有需要的可以去github上拉取直接用。



正常的申请流程,也就是6.0以上手机的申请流程是跳到一个自己封装的空白权限申请界面,然后跳转到系统自带的悬浮窗申请界面进行权限申请,不管用户给不给权限,都关闭当前空页面。由于系统不同,所以在不同手机上看到的权限申请页面颜值可能就会不一样。



2.如何实现悬浮窗可随手指拖动

思路非常简单,监听悬浮窗那个imageView的onTouchListener即可,在刚点击的ACTION_DOWN事件中记录当前的x,y位置,然后在每次移动后获取到本次移动的位置,二者相减就是需要移动的位置,这是自定义view的最基本操作了。


3.如何实现悬浮窗左右边的吸顶效果

思路也非常简单,监听到手指抬起的动作后,判断当前位置是靠近左边还是右边,靠近左边就以位置动画的方式平移到左边,靠近右边就平移到右边。


4.核心,添加悬浮窗的方法

中规中矩,按照android官方的方式添加即可。即获取系统的WindowManager,设置好参数,调用windowManager的addView()方法添加。需要注意的是,在隐藏悬浮窗的时候,最好是移除一下,下次需要显示的时候再添加。



坑点一览,满满的干货

坑点一  多次添加view会导致crash

       这个是很多人都会碰到的,有时候没办法保证只添加一次,就算给了flag也会因为系统原因导致这个flag不准确。所以我在使用的时候就碰到了多次添加了的情况,最后的结果就是crash,解决方案就是在addView方法上加一个try...catch捕获住该异常,妥妥的安全感。


坑点二  oppo r9(5.1.1)上是有权限的,一直给返回没权限

       这段代码是处理6.0以下权限申请的,上面红色部分是原来的代码,下面绿色部分是改过之后的。由于oppo在6.0以下每次检测悬浮窗权限都给false,导致项目中每次都弹框让用户去给权限,最后干脆就oppo 6.0以下直接默认都有权限好了。因为事实也如此,除了小米,其他品牌6.0以下都是有权限的。


坑点三  如何处理输入法和悬浮窗的层级关系

       微信里的悬浮窗是在输入法之下的,所以交互的同学也要求咱们的悬浮窗也要在输入法之下。我查看了一下WindowManager源码,我悬浮窗的优先级TYPE_APPLICATION_OVERLAY,上面大字写着明明是在输入法之下的,但是实际表现是在输入法之上了。


       这个问题也是折腾了半天,在源码里找了半天找得是天昏地暗,终于功夫不负有心人,还真让我找着了,windowManager是有一个flag专门用来设置悬浮窗和输入法的关系的,但是由于之前没设置,所以导致最后默认的输入法还是在悬浮窗之下。


既然源头找到了,设置起来就很简单了,如下图,设置好flag_alt_focusable_im这个flag即可。后面发现有一个帖子对flag总结得非常好,这里也推荐给大家,有想了解其他flag的同学可以去看一下这篇帖子:blog.csdn.net/qq_33275597…


坑点四  如何处理退出app或者按home键的时候关闭悬浮窗

       这也是谷歌坑人的地方,都没地方设置这个悬浮窗是否只用到app内,所以默认在桌面上也会显示自己的悬浮窗。比如在微信里显示其他app的悬浮窗,这种糟糕的体验可想而知,用户不给你卸载就真是奇迹了。为了解决这个问题,最初的实现方式是对所有经过的activity进行记录,显示就加1,页面被挂起就减1,如果减到当前计数为0时说明所有页面已经关闭了,就可以隐藏悬浮窗了。


但是实际上这么做还是有问题的,在部分手机上如果是在首页按返回键的话仍然不能隐藏,这个又是系统级的兼容性问题。为了解决这问题,我后面又做了一个处理,通过注册registerActivityLifecycleCallbacks监听app的前后台回调,检测到如果当前首页被销毁时,应该将悬浮窗进行隐藏。


坑点五  多次点击悬浮窗以后,打开多个页面

       如果你的悬浮窗点击事件是打开页面的话,这里需要注意了,别忘了将这个打开的页面的启动模式设置为singleTop或者是singleTask,从而复用同一个,远离一直按返回的地狱操作。

android:launchMode="singleTop"复制代码

android:launchMode="singleTask"复制代码


总结:悬浮窗的坑,只有真正做过的人才懂,不过还好现在已经有很多现成的第三方代码可以CV。但是就算是CV了也不代表可以高枕无忧,应用场景不同总还是会遇到其他的问题,我这里总结了自己遇到的部分坑点,以后还有的话会继续更新,我相信踩的坑越多,成长才会越快。我也希望自己的工作经历能够帮助到大家少走弯路,就像我借鉴了前辈们的成果一样。


demo地址:github.com/dongrong-fu…


文章分类
Android
文章标签