β

Google也看不下去被玩坏的悬浮窗了么?

子勰的博客 230 阅读

现象

使用全局悬浮窗的应用,在Android 7.1.1 (Android N MR1)上会crash,crash 堆栈类似下面:

E/AndroidRuntime: FATAL EXCEPTION: main
     android.view.WindowManager$BadTokenException: Unable to add window -- window android.view.ViewRootImpl$W@32622a4 has already been added
         at android.view.ViewRootImpl.setView(ViewRootImpl.java:691)
         at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)
         at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:93)
         at com.tencent.ysdk.module.icon.impl.a.g(Unknown Source)
         at com.tencent.ysdk.module.icon.impl.floatingviews.q.onAnimationEnd(Unknown Source)
         at android.view.animation.Animation$3.run(Animation.java:381)
         at android.os.Handler.handleCallback(Handler.java:751)
         at android.os.Handler.dispatchMessage(Handler.java:95)
         at android.os.Looper.loop(Looper.java:154)
         at android.app.ActivityThread.main(ActivityThread.java:6119)
         at java.lang.reflect.Method.invoke(Native Method)
         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

同时在堆栈附近有下面这样一句话:

Adding more than one toast window for UID at a time

背景

最近在开发一个悬浮窗功能,为了方便应用开发者的调用,个人采用了使用全局悬浮窗的方案;同时为了突破厂商的限制,同时又使用了将悬浮窗的type设置为Toast类型。下面是事例代码:

WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.format = PixelFormat.RGBA_8888;
params.gravity = Gravity.LEFT | Gravity.TOP;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
	params.type = WindowManager.LayoutParams.TYPE_TOAST;
} else {
	params.type = WindowManager.LayoutParams.TYPE_PHONE;
}

今天上午Google提示Android 7.1.1可以升级更新,然后就愉快的更新了系统,然后悲催的发现开发中的版本忽然crash了,其中上面的现象中已经说了问题现象。

解决思路

确定问题起因

根据crash的现象,感觉确定是在addView的时候出了问题。然后最近有调整的只有两个方面:

原则上不是这个问题。为了缩小范围排除问题范围,因此先使用代码调整前的版本验证。结果发现问题依然存在,因此确定不是因为代码引起的。

使用另外一台同机型的 Android 7.0的设备验证,相同的包也不会出问题,因此基本上确认就是更新系统引起的。但是google应该不会把这么大的bug遗留下来,那就有可能是有一些变化和调整。

进一步分析原因

结合上面的crash堆栈和问题提示,个人感觉更有可能和 Adding more than one toast window for UID at a time 这句话有关,去google搜索一下,排在第一位的就是关于Android源码的说明:

恩,看来滥用悬浮窗的问题已经很严重了,基本确认这次遇到的异常就是这个问题引起的。

解决方案

根据上面官方的说明,这个变更应该只涉及到Toast,因此只需要保证在7.1.1以上不滥用Toast即可。

临时解决方案

在代码中添加对于7.1.1以上版本的判断,7.1.1以上版本用回phone。代码如下:

WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.format = PixelFormat.RGBA_8888;
params.gravity = Gravity.LEFT | Gravity.TOP;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    if(Build.VERSION.SDK_INT > 24){
        params.type = WindowManager.LayoutParams.TYPE_PHONE;
    }else{
        params.type = WindowManager.LayoutParams.TYPE_TOAST;
    }
} else {
    params.type = WindowManager.LayoutParams.TYPE_PHONE;
}

然后测试问题确实已经解决~

最终解决方案

通过使用phone类型确实解决了crash的问题,但是没有解决厂商的限制,等厂商都升级到7.1.1以后,我们的悬浮窗弹出成功率又会大幅下降。由于我们的实际诉求本来就不是要添加全局的悬浮窗,之所以选择悬浮窗是为了降低第三方的接入成本。在目前这种前提下我们只好添加与第三方的交互,在新的版本调整实现方式,弃用全局悬浮窗,改为在应用内,通过应用添加悬浮窗实现。

题外话

目前对于全局的悬浮窗,不管是厂商还是官方,都已经出手开始限制,可见悬浮窗已经彻底被玩坏了~

作者:子勰的博客
屌丝程序猿,鸡血攻城狮;努力学技术,潜心做精品!
原文地址:Google也看不下去被玩坏的悬浮窗了么?, 感谢原作者分享。

发表评论