6.0之前(APi<23)
google原生为:只要manifest中注册了权限,用户安装后就默认授予了所声明的权限。
中国制造:用户安装应用后可以取消权限的授予
解决办法:
1.在使用部分权限时进行权限查询,查询所需权限是否仍是授予状态
2.进行错误抓取,保证程序活跃,进行错误处理,包括try,catch,检查返回数据
6.0之后(API>=23)
google原生与中国制造基本一致:加入了运行时权限概念,权限大致分为普通权限和危险权限。
中国制造:可能会有用户拒绝权限申请但是没有返回或返回授予成功的情况,此情况需要额外进行处理。
对于运行时权限应用必须在使用的时候进行申请,用户自主选择是否授予。如果拒绝授予应用亦不会crash,只是用户无法使用这一部分功能。
危险权限一共有9组24种,只要用户授予了每一组内的任一权限,对应的权限组都会被授予。
对应方法:
查询权限
public static int checkSelfPermission(Context context,String permission)
申请权限
public static void requestPermissions(final Activity activity, final String[] permissions,final int requestCode)
申请结果返回
public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults)
当权限申请被拒绝后,该方法将返回true,我们可以在此时对所需权限进行详细说明
public static boolean shouldShowRequestPermissionRationale(Activity activity,String permission)
附加知识:自定义权限
声明:
<permission android:name="com.example.selfpermissondemo.permisson.OPEN_ACTIVITY" android:protectionLevel="normal" android:label="自定义权限"/>
使用例子:
<activity android:name=".PermissionActivity" android:permission="com.example.selfpermissondemo.permisson.OPEN_ACTIVITY"> <intent-filter> <action android:name="com.example.selfpermissondemo.permission.activity"></action> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> </activity>
国产ROM
途径:可以使用AppOpsManager#noteOp 类进行权限检测
但是国产ROM的此类被@hide标注,开发者无法直接使用,可以使用google在V4包推出的AppOpsManager 类进行权限检测,该类从API4向后兼容。
权限检测例子:
AppOpsManagerCompat 在API<23的版本下无法正常使用会全部返回true或MODE_IGNORED
API>=23:
/** * 检测权限,小米上使用 * * @param context * @param permission * @return */ *
*private static boolean hasSelfPermissionForXiaomi(Context context, String permission) { *
// 将permission转换成OP_ String permissionToOp = AppOpsManagerCompat.permissionToOp(permission);
if (permissionToOp == null) {
// 不支持的权限,或者是normal permission
return true;
}
int noteOp = AppOpsManagerCompat.noteOp(context, permissionToOp, android.os.Process.myUid(), context.getPackageName());
// AppOpsManagerCompat 与 checkSelfPermission都检测过则表明权限被开启
return noteOp == AppOpsManagerCompat.MODE_ALLOWED && ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; }
API<23
使用反射对AppOpsManager#noteOp 类进行访问
/** * 通过反射调用 AppOpsManager#noteOp * * @param context * @param op * @return */
public static boolean noteOp(Context context, int op) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
if (appOpsManager != null) {
try {
Method method = AppOpsManager.class.getDeclaredMethod("noteOp", Integer.TYPE, Integer.TYPE, String.class);
int noteOp = (int) method.invoke(appOpsManager, op, Process.myUid(), context.getPackageName());
return noteOp == AppOpsManager.MODE_ALLOWED;
}
catch (Exception e) {
e.printStackTrace();
} } }
return false;
}
国产ROM总结: 对于权限检测:
- Build.VERSION.SDK_INT >= 23时,直接使用我们上面的
hasSelfPermissionForXiaomi
方法 - Build.VERSION.SDK_INT < 23时,使用我们上面的反射方法
noteOp
。
对于权限申请:
- Build.VERSION.SDK_INT >= 23时,使用上节介绍的标准运行时权限申请方式
- Build.VERSION.SDK_INT < 23时,没有办法申请API,只能通过引导用户跳转权限设置页进行配置。
特殊权限
SYSTEM_ALERT_WINDOW
和WRITE_SETTINGS
这两种权限为特别敏感的权限,需要用户在系统设置界面进行手动授权
例子:
// 定义权限申请request code
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE= 123;
public void requestPermission() {
// 通过Settings.canDrawOverlays(this)可以检测是否包含SYSTEM_ALERT_WINDOW权限
if (!Settings.canDrawOverlays(this)) {
// 申请权限,跳转到系统权限设置页面
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
} }
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 在onActivityResult中判断是否授权成功
if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
if (Settings.canDrawOverlays(this)) {
// 授权成功
} } }
附加:悬浮窗实现可以使用LayoutParams.TYPE_TOAST
替换 LayoutParams.TYPE_SYSTEM_ALERT
,LayoutParams.TYPE_TOAST
的使用不需要权限。(LayoutParams.TYPE_TOAST限制就是Android 4.4 (API level 18)及以下使用TYPE_TOAST无法接收触摸事件)
补充:“不依赖Activity”进行权限申请
可自己注册一个activity专门用作申请权限使用
demo中我会用到
注意
当系统要求用户授予权限时,用户可以选择指示系统不再要求提供该权限。这种情况下,无论应用在什么时候使用 requestPermissions()
再次要求该权限,系统都会立即拒绝此请求。系统会调用您的 onRequestPermissionsResult()
回调方法,并传递 PERMISSION_DENIED
,如果用户再次明确拒绝了您的请求,系统将采用相同方式操作。这意味着当您调用 requestPermissions()
时,您不能假设已经发生与用户的任何直接交互。