转载

关于Android 6.0 运行时权限

Android 6.0 (API level 23) 已经发布很长一段时间了,其中一个很大的改进就是 运行时权限 。我之前就在知乎回答过一个问题 iPhone 到底爽在哪里?

我说,iPhone上的App都是默认下载安装的,然后运行App时需要什么权限就弹窗向我申请,这对用户来说就非常好。因为我不想给App权限就不给,而Android 6.0以前是这样的,我下载了一个App安装,系统就弹出这个App需要使用的全部的权限,就给我看一下,我需要这个App 的话,只能同意所有的权限都给这个App,要么我不安装这个App

Android 6.0以前的权限管理应该是我对Android最不满的功能,6.0中 Google终于来解决这个问题了。

下面我就来好好聊聊这个运行时权限管理是怎么回事

权限分类

在6.0中Android把所有的权限从 逻辑上 分成了两类: 常规(normal)和危险(dangerous)

  • 常规权限 指的是那些不会直接获取你隐私的权限,如果你在AndroidManifest.xml文件中列出了这些权限的话,系统会自动授权给你。 这里是normal权限列表 ,有很多
  • 危险权限 就是那些能获取你隐私信息,或者可能会影响你的文件等的操作。比如读取你的联系人、使用你的摄像头和读取你的日历等等

权限组

这里是危险权限列表:

关于Android 6.0 运行时权限

从上图中我们可以看到,Android系统把危险权限分了9大组,这样也是为了简化权限的申请机制。如果你申请了 android.permission.READ_CONTACTS 读取联系人的权限, 那么6.0 系统就会把这一组中其他的权限也打包给你 。我觉得这个和iOS的隐私管理机制非常相似,在iOS系统设置的“ 隐私->通讯录 ”中可以看到,如果你给一个App通讯录的权限,那么这个App既可以读也可以写的

Android 6.0里面只有危险权限才需要运行时获取的

申请权限

tartgetSdkVersion

说到申请权限,先要说一下 targetSdkVersion 这个字段,这个字段一般定义在build.gradle文件中的。这个对App来说很重要!但是是什么意思呢?

假如说 targetSdkVersion 22 ,安装好以后Android系统就知道了这个App在系统API 22以下都测试过了并且能正确运行的,但是在23以上并不可以正确运行的,假如说这个App运行在了Android 6.0系统上,那么Android就会对这个App很“照顾”,兼容它正确运行。比如,6.0系统会把App申请的所有权限都默认给这个App,处理的逻辑和6.0一下的系统是一样的

ActivityCompat

android.support.v4.app.ActivityCompat 这个类是App 向系统申请权限主要的工具,而且兼容了各种系统版本

  • ActivityCompat.requestPermissions 向系统申请一个或一组权限
  • ActivityCompat.checkSelfPermission App检查自己是否有某个权限
  • ActivityCompat.shouldShowRequestPermissionRationale 判断弹出对话框中是否含有“不再询问”的选择框

关于Android 6.0 运行时权限

申请权限的步骤:

  1. 你要有一个运行Android 6.0系统的设备
  2. 将App的 targetSdkVersion 设置为23
  3. AndroidManifest.xml 中申请的并且是危险的所有权限都列出来,用 ActivityCompat.requestPermissions 方法向系统申请权限
  4. 在所在的Activity中Override onRequestPermissionsResult 方法接受系统权限申请的回调
  5. 处理回调,比如用户拒绝了某个权限,这时App可以弹出一个对话框描述一下App为何需要这个权限等等

targetSdkVersion 小于23

假如你的App的 targetSdkVersion 小于23,但是安装到了Android 6.0系统上了,会怎么样呢?会崩溃吗?

别担心,Android开发团队已经考虑到这一点了,如果 targetSdkVersion 小于23的话,就表示你的App并没有在新的运行时权限系统上测试过, 此时Android系统会把你申请的全部权限都给你

但是!!!用户依然可以进入App的设置界面把权限关闭!!

关于Android 6.0 运行时权限

此时你还能用这个权限么?经过我的测试,是不可以了。

所以,如果App的 targetSdkVersion 小于23并且运行在Android 6.0系统上,怎么去检测用户关闭了权限呢?伟大的stackoverflow告诉我们: android.support.v4.content.PermissionChecker 可以帮我们解决这个问题。这个类的文档中有这个一段:

For apps targeting API lower than android.os.Build.VERSION_CODES.M these permissions are always granted as such apps do not expect permission revocations and would crash. Therefore, when the user disables a permission for a legacy app in the UI the platform disables the APIs guarded by this permission making them a no-op which is doing nothing or returning an empty result or default error.

PermissionChecker.checkSelfPermission 方法就是用于检查App自身有没有某一个权限,这个方法的返回结果只有三种:

  • PERMISSION_GRANTED : 已授权
  • PERMISSION_DENIED : 没有被授权
  • PERMISSION_DENIED_APP_OP : 没有被授权

PERMISSION_DENIEDPERMISSION_DENIED_APP_OP 都表示没有被授权,但是它们的区别就在于 targetSdkVersion 的值,如果 targetSdkVersion 小于23,就返回 PERMISSION_DENIED_APP_OP ,否则就返回 PERMISSION_DENIED

因此,如果你的App的 targetSdkVersion 小于23,但是运行在Android 6.0及以后的系统上,你可以用 PermissionChecker.checkSelfPermission(context, permission) == PermissionChecker. PERMISSION_DENIED_APP_OP 来检查App是否有某一个权限

总结

如果App的 targetSdkVersion 小于23(Android 6.0以前),那么 ContextCompat#checkSelfPermissionContext#checkSelfPermission 方法的返回结果都是错误的,因为它们总是返回0( PERMISSION_GRANTED )。即使App运行在Android 6.0上并且用户在设置中关闭了App的权限,上面两个方法返回的结果也是0

上面也说到,Android 6.0系统上,用户是可以关闭App权限的,所以并不是说App的 targetSdkVersion 小于23就可以不用关心权限问题了:

  • Android < 6.0:系统会给App所有的权限
  • Android >= 6.0 && targetSdkVersion < 23:系统会默认给予App所有的权限,但是用户可以去设置中关闭权限。这时你需要使用 PermissionChecker.checkSelfPermission 来检测App是否有某一个权限
public boolean selfPermissionGranted(Context context, String permission) {
    // Android 6.0 以前,全部默认授权
    boolean result = true;

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       if (targetSdkVersion >= Build.VERSION_CODES.M) {
            // targetSdkVersion >= 23, 使用Context#checkSelfPermission
            result = context.checkSelfPermission(permission)
                    == PackageManager.PERMISSION_GRANTED;
        } else {
            // targetSdkVersion < 23, 需要使用 PermissionChecker
            result = PermissionChecker.checkSelfPermission(context, permission)
                    == PermissionChecker.PERMISSION_GRANTED;
        }
    }
    return result;
}

获取App的targetSdkVersion值:

try {
    final PackageInfo info = context.getPackageManager().getPackageInfo(
        context.getPackageName(), 0);
    targetSdkVersion = info.applicationInfo.targetSdkVersion;
} catch (PackageManager.NameNotFoundException e) {
    e.printStackTrace();
}

最后

不得不说,慢慢的从Android 5.0开始,Google慢慢的缩减了Android开放策略,以前的Android真的是可以为所欲为,监听系统各种变化,甚至一个App被切换到后台,它任然可以获取到当前正在运行的App(用户正在使用的),这个Api可以轻松的获取用户的隐私信息啊,太可怕了。

从Android 6.0开始,运行时权限、Doze模式以及App Standby,Android 7.0中对Doze模式加强,以及取消了很多比如 CONNTENCTIVITY_ACTIONACTION_NEW_PICTUREACTION_NEW_VIDEO 广播

原文  http://bxbxbai.github.io/2016/05/27/android-runtime-permission/
正文到此结束
Loading...