悬浮窗设置TYPE_TOAST出现崩溃的解决方案

关于悬浮窗设置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)来判断应用是否授权了悬浮窗显示权限。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章