Android

Android 运行时权限RuntimePermission

2017-05-19  本文已影响192人  JustDo23

引言:运行时权限是版本升级的一个更新点,学起来挺容易的,写一篇笔记感觉真费劲。

时间:2017年04月18日23:45:50

作者:JustDo23

01. 前言

运行时权限Android 6.0 变更中尤为突出的一点。6以下的手机在安装时候提示权限列表,用户全部同意后才能安装程序;6以后的手机直接安装,在运行过程中动态的向用户申请所需权限,另外用户可以在设置界面对程序的各个权限进行管理。

Android 中的权限可以分为三类:

普通权限不涉及用户隐私,在AndroidManifest.xml中声明即获取;危险权限涉及用户的隐私,在用户授权之后方能使用。另外,Google 对危险权限进行了分组,当某一个组中的某一个权限被授权之后应用同时就获取到了整个组的所有权限。而且,调用 API 申请权限时系统将向用户显示一个标准对话框,该对话框上的提示权限说明是针对整个组的说明,应用无法配置或更改此对话框。

02. 声明权限

官方文档中提到:为了保护系统的完整性和用户隐私权,Android 在访问受限的沙盒中运行每款应用。如果应用需要使用其沙盒以外的资源或信息,则必须明确请求权限。根据应用请求的权限类型,系统可能会自动授予权限,也可能会要求用户授予权限。可以在应用清单中列出相应的权限,声明应用需要此权限。

声明权限,将<uses-permission>元素置于顶级<manifest>元素的子项。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.snazzyapp">

    <uses-permission android:name="android.permission.SEND_SMS"/>
    
    <application ...>
        ...
    </application>

</manifest>

03. 检查权限

/**
 * Determine whether you have been granted a particular permission.
 *
 * @param permission The name of the permission being checked.
 */
ContextCompat.checkSelfPermission(@NonNull Context context, @NonNull String permission)

以上为核心 API 检查是否拥有权限,简单封装如下:

/**
 * 检查是否具有某权限
 *
 * @return true, 有权限 false,无权限
 */
private boolean checkPermission(Context context, String permission) {
  boolean checkPermission = false;
  int permissionCheck = ContextCompat.checkSelfPermission(context, permission);// 检查权限
  switch (permissionCheck) {
    case PackageManager.PERMISSION_GRANTED:// 有权限
      checkPermission = true;
      break;
    case PackageManager.PERMISSION_DENIED:// 无权限
      checkPermission = false;
      break;
  }
  return checkPermission;
}
// 检查日历权限
ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR);

04. 请求权限

/**
 * 请求指定的权限集合
 *
 * @param activity The target activity.
 * @param permissions The requested permissions. Must me non-null and not empty.
 * @param requestCode Application specific request code.
 */
ActivityCompat.requestPermissions(
                      final @NonNull Activity activity,
                      final @NonNull String[] permissions,
                      final @IntRange(from = 0) int requestCode);
// 请求一个相机权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 23);
// 同时请求三个权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_CALENDAR, Manifest.permission.READ_SMS}, 32);

05. 请求响应

/**
 * Callback for the result from requesting permissions.
 *
 * @param requestCode The request code.
 * @param permissions The requested permissions. Never null.
 * @param grantResults The grant results.
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
        @NonNull int[] grantResults) {
    switch (requestCode) {
      case 23:
        if (grantResults.length > 0) {
          if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            ToastUtil.showShortToast(MainActivity.this, "授权");
          } else {
            ToastUtil.showShortToast(MainActivity.this, "拒绝");
          }
        }
        break;
      case 32:
        break;
    }
}

06. 提供解释

/**
 * Gets whether you should show UI with rationale for requesting a permission.
 *
 * @param activity The target activity.
 * @param permission A permission your app wants to request.
 * @return Whether you can show permission rationale UI.
 */
public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
        @NonNull String permission) {
    if (Build.VERSION.SDK_INT >= 23) {
        return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission);
    }
    return false;// 低版本直接返回 false
}

这部分的内容Android M 权限最佳实践这篇文章写得很好。

07. 官方例子

if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {
        // No explanation needed, we can request the permission.
        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);
    }
}

08. 兼容问题

首先明确一下在什么情况下需要使用运行时权限动态申请:

  1. 危险权限
  2. Android 版本 >= 6.0
  3. targetSdkVersion >= 23

在这三个条件同时具备的情况下必须使用运行时权限机制。当targetSdkVersion < 23的时候不用调用运行时权限机制,当targetSdkVersion >= 23的时候必须调用运行时权限机制以确保程序不会出现崩溃。另外,运行时权限机制的相关 API 都是在support.v4包下,说明做了低版本兼容,相关 API 运行在低版本时候均有默认的返回值。在此基础上兼容适配问题可以考虑从两个因素Android 版本targetSdkVersion入手,用一张表来简单分析兼容适配问题:

targetSdkVersion Android 版本 兼容适配
targetSdkVersion < 23 SDK < 23 安装时授权,正常使用
targetSdkVersion < 23 SDK >= 23 直接安装并授权,用户管理权限,可能崩溃
targetSdkVersion >= 23 SDK < 23 安装时授权,运行时权限 API 有默认返回值,正常使用
targetSdkVersion >= 23 SDK >= 23 直接安装,运行时授权,用户管理权限,正常使用

从表格中看出如果应用targetSdkVersion < 23运行在Android 6.0的手机上,由于用户可以自主管理权限,取消某些授权,便会引起程序崩溃。因此,尽快提升目标版本同时添加运行时权限判断申请 API 是很有必要的。

注意:对于以 Android 6.0 或更高版本为目标平台的应用,请务必在运行时检查和请求权限。即使不以 Android 6.0 为目标平台,也应该在新权限模式下测试应用。

09. 其他小点

10. 奇思怪想

11. 推荐文章

有几篇非常不错的文章值得阅读学习:

  1. 张鸿洋 Android 6.0 运行时权限处理完全解析
  2. Android M 权限最佳实践
  3. 严振杰 Android 6.0 运行时权限管理最佳实践
  4. android permission权限与安全机制解析(下)
  5. 聊一聊 Android 6.0 的运行时权限
  6. Android 权限管理 —— AppOps
上一篇下一篇

猜你喜欢

热点阅读