Android悬浮窗各版本系统适配方案

悬浮球是什么?

        所谓的悬浮球或者悬浮窗,就是置于屏幕最上层的一个可操作控件,在本应用处于前台或者后台的时候均可见、可操作。360手机助手、应用宝均有此功能。悬浮球功能多见于提供手机清理、加速功能的应用,因其始终展示在屏幕顶部,方便用户触达,也是增强用户粘性、提升App活跃的不错手段。

 

实现原理是什么?

  悬浮球的实现就是通过WindowManageraddView方法,把一个自定义View添加到Window,关键点在于addView时候给自定义View指定的LayoutParams中的type参数。

  其实系统提供了标准类型——LayoutParams.TYPE_SYSTEM_ALERT,用来实现悬浮球功能使用,但是这种类型需要申请权限:android.permission.SYSTEM_ALERT_WINDOWAndroid6.0之前还好,因为是静态权限,申请就申请,反正应用安装的时候用户就给授权了,但Android6.0引入了运行时权限,这个权限必须在使用时动态申请,这下就麻烦了。因为产品经理希望用户的操作越简单越好,最好不需要动态申请任何权限就能实现各种功能。

  那如何在Android6.0及以后系统上绕开运行时权限实现悬浮球呢?那就是把type参数的值指定为LayoutParams.TYPE_TOAST。这一点网上有很多文章介绍(譬如:点击打开链接),欲了解细节,请自行搜索。其根本就是利用系统源码漏洞,绕开了权限申请。

  这样一来,好像问题都解决了,剩下的就是如何做强你的进程保活,好让你的悬浮球能够一直显示,做到像360手机助手、应用宝一样强大。

  今年初,公司推出了基于Android8.0系统的新机,在新机器上运行项目的时候发现悬浮球又出不来了。这又是为什么呢?经过查看Google官方8.0更新文档,发现了问题所在(详情请查看官方文档:点击打开链接)。原因是TYPE_SYSTEM_ALERTTYPE_TOAST等类型在8.0上被废弃了,引入了新类型TYPE_APPLICATION_OVERLAY。那我们就把代码修改为运行在8.0系统上时,类型参数为TYPE_APPLICATION_OVERLAY,是不是就可以了?实际上,修改后悬浮球依然显示不出来,或者应用在前台时候,悬浮球会显示,应用切换到后台后,悬浮球一会儿就消失了。没招了,只能看看360、应用宝怎么做的吧,毕竟人家是大厂!

  经过观察,只要清单文件里注册了android.permission.SYSTEM_ALERT_WINDOW权限,在设置-》应用管理-》应用详情就会多出来Display over other apps这一项。在7.0上,都是默认关闭的,但悬浮球照样可以显示。8.0上,360竟然是默认开启的,悬浮球可以显示。应用宝是默认关闭的,悬浮球也出不来。那我们不禁要问了,360是如何悄悄打开的?打开之后又做了什么其它工作呢?我们同时发现360在初次安装打开的时候并没有动态申请任何权限,而在系统的权限管理里面,360的所有权限已经开启了。有理由怀疑360targetSdkVersion设置的值应该小于23Android6.0)。

  运行aapt  dump  badging  E:\com.qihoo.appstore_300070177.apk命令,可查看到360targetSdkVersion=19,所以360悬浮球在8.0上无需做额外工作就能够显示出来,而且所有的权限在初次安装就被授予了,不需要运行时申请。应用宝的targetSdkVersion=23,安装成功后Display over other apps这一项默认是关闭的,打开悬浮窗功能时,会弹出“去授权”的弹框。很显然,应用宝并没有绕开权限申请的办法。至此,360的方法是有点变态的,对我们来说并不适用。那就只能跟应用宝一样,老老实实的去申请权限了。

  这样就完美了吗?No,并不完美。

  有一天,产品经理拿过来一部土豪金的手机说,怎么我们的应用在这款手机上,一打开悬浮窗功能就崩溃了。我淡淡地瞅了一眼,这款土豪金是面临倒闭风险的金立生产的G7。这时候,测试也跑过来了,拿着一款带着粉红色卡哇伊外壳的手机说,我们的应用在这手机上,打开悬浮窗的时候就崩溃了,悬浮窗出不来。我又淡淡地瞅了一眼,这款手机是刚冲击完上市,在印度闹得正欢的小米生产的5X

  经观察,这两款手机都是7.1系统,貌似之前还没用7.1系统测试过。两部手机上的崩溃日志都是相同的:android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?很明显,7.1上系统修复了这个大家绕开权限显示悬浮球的bug,但还没有像8.0那样推出新类型TYPE_APPLICATION_OVERLAY。那怎么办呢?继续参考大厂的做法吧,毕竟人家是行业标杆。360的做法不具有参考价值,就不去看它了。应用宝在7.1上的表现跟8.0上是一致的,都是老老实实地去引导用户授权。那我们就比葫芦画瓢,跟着应用宝走了。

        至此,悬浮球功能在各版本系统上的适配就告一段落了。最后总结如下:

        14.4以下使用TYPE_SYSTEM_ALERT,需要权限android.permission.SYSTEM_ALERT_WINDOW

        24.47.0,使用TYPE_TOAST,不需要额外权限;(既然TYPE_TOAST不需要任何权限,那4.4以下为什么不使用呢?因为4.4以下,TYPE_TOAST类型的悬浮球收不到触摸事件)

    37.1上,使用TYPE_TOAST无效,悬浮球并不会显示,使用TYPE_SYSTEM_ALERT,动态申请android.permission.SYSTEM_ALERT_WINDOW权限;

    48.0对于以上两种type都废弃了,需要使用TYPE_APPLICATION_OVERLAY,并需要运行时权限android.permission.SYSTEM_ALERT_WINDOW

        关键代码如下:

floatingBallLayoutParams = new LayoutParams();

                    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) {

                        floatingBallLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

                    } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) {

                        floatingBallLayoutParams.type = LayoutParams.TYPE_SYSTEM_ALERT;

                    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

                        floatingBallLayoutParams.type = LayoutParams.TYPE_TOAST;

                    } else {

                        floatingBallLayoutParams.type = LayoutParams.TYPE_SYSTEM_ALERT;

                    }


  最后,实际上悬浮球功能并没有多复杂。悬浮球就是一个自定义View,重写onTouchEvent,处理下触摸事件。另外UI有要求的话,使用贝塞尔曲线做个波浪起伏的动画。如果按照标准流程去申请权限显示悬浮球的话,工作就很简单了。之所以复杂,就是因为产品在设计的时候,往往喜欢利用系统设计的漏洞和缺陷去做一些貌似黑科技的功能,或者简化一些系统要求的操作。以上介绍的适配方法,并不保证能够真正适配市面上的所有机型,依然需要大量的去测试。

 

 欢迎转载,转载请注明出处。如有错误,欢迎在评论区指正。https://blog.csdn.net/mayibanjia216/article/details/80655462

你可能感兴趣的:(Android)