1、现象
目前测试中发现的现象有两种:
- 在使用Type Toast悬浮窗的同时,使用了Toast,必现崩溃,即使catch 了Throwable也无法解决,但是将悬浮窗addView的代码注释之后则不出现崩溃;
- 如果只有Type Toast悬浮窗,不会出现崩溃,3.5s后自动消失
关键log如下:
WindowManager$BadTokenException: Unable to add window -- window android.view.ViewRootImpl$W@363f7b1 has already been added
crash log.png2、原因分析
从查看google的代码发现,在25版本上有这么一个提交:
https://android.googlesource.com/platform/frameworks/base/+/dc24f93
从注解可以大致了解到,google在该版本开始对TYPE TOAST进行管控,防止一个应用的悬浮窗一直悬浮在另一个应用上造成干扰。
commit.png在windowManager#addView的流程中:
windowManager#addView ->
WindowManagerImpl#addView ->
WindowManagerGlobal#addView ->
ViewRootImpl#setView ->
WindowManagerService#addWindow
在addView中会给window添加上额外的属性(这里每个版本可能会有不同的实现,这里是android7.1.1的实现)
adJustWindowParamsLw.png手机上mPolicy可以直接查看PhoneWindowManager的代码实现,此时将window的params属性添加了hideTimeoutMilliseconds,值为3500ms,即最多显示3.5s
hideTimeoutMilliseconds.pngWindowManagerService#addWindow中会依据条件判断是否满足重复添加toast的条件,满足则返回ADD_DUPLICATE_ADD
WindowManagerService#addWindow.png判断当前应用(UID)是否还有Type toast的悬浮窗在展示
canAddToastWindowForUid.png在ViewRootImpl中拿到这个结果后则抛出异常:
ViewRootImpl#setView.png在WindowManagerGlobal中捕获并继续抛出
WindowManagerGlobal#addView.png
如果没有重复添加TYPE TOAST,则显示,并设置延迟消息,延迟时间即为params中设置的最长显示时间
add_ok.png时间到了之后则强制隐藏
window_hide.png3、解决方案
目前的解决方案就是在Android7.1.1上使用需要申请system alert window权限的系统级悬浮窗,或者是层级较低的TYPE_PHONE悬浮窗。
网友评论
mWM = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mParams = new WindowManager.LayoutParams();
// XXX This should be changed to use a Dialog, with a Theme.Toast
// defined that sets up the layout params appropriately.
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = R.style.Animation_Toast;
if (Build.VERSION.SDK_INT >= 24) {
params.type = WindowManager.LayoutParams.TYPE_PHONE;
} else {
params.type = WindowManager.LayoutParams.TYPE_TOAST;
}
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
mHandler = new Handler(Looper.getMainLooper());
}
我们项目这样使用还是会报这个错误,很奇怪
https://aligames.open.uc.cn/document/doc/detail/15