Android悬浮窗权限“android.permission.SYSTEM_ALERT_WINDOW”判断是否开启问题
2018-02-04 17:26
423 查看
最近在android 8.0上遇到了一个判断悬浮窗权限是否开启的问题,当在一个界面弹出dialog提示用户开启悬浮窗权限,用户点击之后,跳转到设置界面开启悬浮窗权限,然后返回该页面,使用google提供的android 6.0以及以后可以使用的接口
上面的代码区分了不同的版本悬浮窗权限的判断方法,在4.4以前是不用判断悬浮窗权限的直接使用就可以了,在4.4到6.0之前,google没有提供方法让我们用于判断悬浮窗权限,同时也没有跳转到设置界面进行开启的方法,因为此权限是默认开启的,但是有一些产商会修改它,所以在使用之前最好进行判断,以免使用时出现崩溃,判断方法是用反射的方式获取出是否开启了悬浮窗权限。在6.0以及以后的版本中,google为我们提供了判断方法和跳转界面的方法,直接使用
从新代码和老代码的对比可以发现,我对8.0的版本做了单独的处理,那就是通过判断mode来判断权限是否开启。为什么要这样了?因为在8.0上,有的手机是区分了mode的,导致使用
AppOpsManager.MODE_ALLOWED —— 表示授予了权限并且重新打开了应用程序
AppOpsManager.MODE_IGNORED —— 表示授予权限并返回应用程序
AppOpsManager.MODE_ERRORED —— 表示当前应用没有此权限
AppOpsManager.MODE_DEFAULT —— 表示默认值,有些手机在没有开启权限时,mode的值就是这个
上面所说的区分mode的值,就是区分了
Settings.canDrawOverlays(context)进行权限开启的判断,结果返回的是false;程序接收到的是权限没有开启,但是到设置里面查看确实是开启了的。而且当你对界面有刷新操作之后(去到其他界面、退出重新进入、点击按钮等等),检测到权限也是开启的。但是当你从界面跳转到设置并开启权限,然后返回界面直接调用
Settings.canDrawOverlays(context)方法判断显示的是未开启。what?这是什么问题了?刚开始猜想会不会是延迟问题了,于是在开启界面多等了一会儿,结果返回还是不行!后面通过查看资料找到了问题的所在,所以在此记录一下。
怎样解决?
首先来看看我原来的判断代码:public static boolean checkFloatPermission(Context context) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) return true; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { try { Class cls = Class.forName("android.content.Context"); Field declaredField = cls.getDeclaredField("APP_OPS_SERVICE"); declaredField.setAccessible(true); Object obj = declaredField.get(cls); if (!(obj instanceof String)) { return false; } String str2 = (String) obj; obj = cls.getMethod("getSystemService", String.class).invoke(context, str2); cls = Class.forName("android.app.AppOpsManager"); Field declaredField2 = cls.getDeclaredField("MODE_ALLOWED"); declaredField2.setAccessible(true); Method checkOp = cls.getMethod("checkOp", Integer.TYPE, Integer.TYPE, String.class); int result = (Integer) checkOp.invoke(obj, 24, Binder.getCallingUid(), context.getPackageName()); return result == declaredField2.getInt(cls); } catch (Exception e) { return false; } } else { return Settings.canDrawOverlays(context); } }
上面的代码区分了不同的版本悬浮窗权限的判断方法,在4.4以前是不用判断悬浮窗权限的直接使用就可以了,在4.4到6.0之前,google没有提供方法让我们用于判断悬浮窗权限,同时也没有跳转到设置界面进行开启的方法,因为此权限是默认开启的,但是有一些产商会修改它,所以在使用之前最好进行判断,以免使用时出现崩溃,判断方法是用反射的方式获取出是否开启了悬浮窗权限。在6.0以及以后的版本中,google为我们提供了判断方法和跳转界面的方法,直接使用
Settings.canDrawOverlays(context)就可以判断是否开启了悬浮窗权限,没有开启可以跳转到设置界面让用户开启,跳转方法就不做说明了。上面的代码看似没有什么问题,但是在使用的过程中,发现有的8.0手机会出现上面所说的问题。所以有了下面的新的判断方法,代码如下:
public static boolean checkFloatPermission(Context context) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) return true; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { try { Class cls = Class.forName("android.content.Context"); Field declaredField = cls.getDeclaredField("APP_OPS_SERVICE"); declaredField.setAccessible(true); Object obj = declaredField.get(cls); if (!(obj instanceof String)) { return false; } String str2 = (String) obj; obj = cls.getMethod("getSystemService", String.class).invoke(context, str2); cls = Class.forName("android.app.AppOpsManager"); Field declaredField2 = cls.getDeclaredField("MODE_ALLOWED"); declaredField2.setAccessible(true); Method checkOp = cls.getMethod("checkOp", Integer.TYPE, Integer.TYPE, String.class); int result = (Integer) checkOp.invoke(obj, 24, Binder.getCallingUid(), context.getPackageName()); return result == declaredField2.getInt(cls); } catch (Exception e) { return false; } } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { AppOpsManager appOpsMgr = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); if (appOpsMgr == null) return false; int mode = appOpsMgr.checkOpNoThrow("android:system_alert_window", android.os.Process.myUid(), context .getPackageName()); return mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_IGNORED; } else { return Settings.canDrawOverlays(context); } } }
从新代码和老代码的对比可以发现,我对8.0的版本做了单独的处理,那就是通过判断mode来判断权限是否开启。为什么要这样了?因为在8.0上,有的手机是区分了mode的,导致使用
Settings.canDrawOverlays(context)方法判断的时候会出问题。下面我们先说说mode代表的意思:
AppOpsManager.MODE_ALLOWED —— 表示授予了权限并且重新打开了应用程序
AppOpsManager.MODE_IGNORED —— 表示授予权限并返回应用程序
AppOpsManager.MODE_ERRORED —— 表示当前应用没有此权限
AppOpsManager.MODE_DEFAULT —— 表示默认值,有些手机在没有开启权限时,mode的值就是这个
上面所说的区分mode的值,就是区分了
AppOpsManager.MODE_ALLOWED和
AppOpsManager.MODE_IGNORED,而
Settings.canDrawOverlays(context)方法只有当mode等于
AppOpsManager.MODE_ALLOWED时才会返回true,所以当mode等于
AppOpsManager.MODE_IGNORED时会返回false,这样就会出现开启了权限,但是判断时却显示没有开启的问题。所以要解决这个问题,就要获取出当前的mode,对mode进行判断,当mode等于
AppOpsManager.MODE_ALLOWED或者mode等于
AppOpsManager.MODE_IGNORED时都是已经开启权限,代码如上。
相关文章推荐
- 随笔:关于权限android.permission.SYSTEM_ALERT_WINDOW
- android M SYSTEM_ALERT_WINDOW权限问题
- Android O 特殊权限SYSTEM_ALERT_WINDOW申请,“permission denied for window type”报错处理
- Android权限问题:Permission is only granted to system apps
- 请教一个问题,Android应用运行时在调用某个API(比如相机)时,系统如何判断这个应用是否具备相应的权限?
- Android 在Service中弹出窗口及SYSTEM_ALERT_WINDOW权限解决方法
- 请教一个问题,Android应用运行时在调用某个API(比如相机)时,系统如何判断这个应用是否具备相应的权限?
- Android如何获取判断是否有悬浮窗权限
- android 6.0之后 android.permission.SYSTEM_ALERT_WINDOW使用方法变动
- Android 广播弹框弹不出来 即使加了android.permission.SYSTEM_ALERT_WINDOW
- 如何判断是否得到"android.permission.PACKAGE_USAGE_STATS"权限
- Android判断某个权限是否开启
- android 特殊权限SYSTEM_ALERT_WINDOW,WRITE_SETTINGS
- android 7.1.1下android.permission.SYSTEM_ALERT_WINDOW改动及BUG
- Android 获取判断是否有悬浮窗权限的方法
- Android权限问题:Permission is only granted to system apps
- Android 判断应用程序获取通知栏权限是否开启,以及如何跳转到应用程序设置界面
- Android权限问题:Permission is only granted to system apps
- 帧动画的大小设置与悬浮窗权限system_alert_window 6.0后需要单独处理
- permission is only granted to system apps--Android manifest权限问题