关于悬浮窗设置TYPE_TOAST崩溃的问题
- 最近在做项目有用到悬浮窗,发现了一个问题,WindowManager.LayoutParams的属性设置为TYPE_TOAST在安卓7.1.1系统出现崩溃。在使用Type Toast悬浮窗的同时,使用了Toast,必现崩溃,即使catch 了Throwable也无法解决,但是假如什么都没做则不会崩溃,不过几秒后就消失了
查了一下资料发现在安卓7.1.1的系统上谷歌限制了开发者对TYPE_TOAST的滥用
google在该版本开始对TYPE TOAST进行管控,防止一个应用的悬浮窗一直悬浮在另一个应用上造成干扰
// If adding a toast requires a token for this app we always schedule hiding
// toast windows to make sure they don't stick around longer then necessary.
// We hide instead of remove such windows as apps aren't prepared to handle
// windows being removed under them.
//
// If the app is older it can add toasts without a token and hence overlay
// other apps. To be maximally compatible with these apps we will hide the
// window after the toast timeout only if the focused window is from another
// UID, otherwise we allow unlimited duration. When a UID looses focus we
// schedule hiding all of its toast windows.
if (type == TYPE_TOAST) {
if (!getDefaultDisplayContentLocked().canAddToastWindowForUid(callingUid)) {
Slog.w(TAG_WM, "Adding more than one toast window for UID at a time.");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// Make sure this happens before we moved focus as one can make the
// toast focusable to force it not being hidden after the timeout.
// Focusable toasts are always timed out to prevent a focused app to
// show a focusable toasts while it has focus which will be kept on
// the screen after the activity goes away.
if (addToastWindowRequiresToken
|| (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
|| mCurrentFocus == null
|| mCurrentFocus.mOwnerUid != callingUid) {
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
}
}
从源码可以看出假如添加了TYPE_TOAST,则显示,并且设置了延迟消息,延迟时间为hideTimeoutMilliseconds
时间到了则强制隐藏
case WINDOW_HIDE_TIMEOUT:{
final windowState window = (WindowState)msg.obj;
synchronized(mWindowMap){
window.mAttrs.flags &= ~FLAG_KEEP_SCREEN_ON;
window.hidePermanentlyLw();
window.setDisplayLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
}
}
break;
时间到了强制隐藏~
我这里解决的方案是判断系统,如果大于7.0的话则使用TYPE_SYSTEM_ALERT来显示
//设置type,系统提示型窗口,在应用程序之上
if (FloatPermissionChecker.checkSDKVersion()) {
mParmas!!.type = WindowManager.LayoutParams.TYPE_TOAST
} else {
mParmas!!.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
}
这样的话就需要手动去开启设置里面的悬浮窗开关,我们代码里面可以这么写去引导用户打开悬浮窗
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
//这句是重点,可以调到对应的应用界面里
intent.data = Uri.parse("package:" + context.activity!!.packageName)
context.startActivityForResult(intent, DeveloperComponent.PERMISSION_REQUEST, null, listener)
然后通过Settings.canDrawOverlays(application)来判断应用是否授权了悬浮窗显示权限。