【 Android 】Android Runtime Permissions 解决方案(适配 Android O)

【 Android 】Android Runtime Permissions 解决方案(适配 Android O)_第1张图片
permissions.png

Android 运行时请求权限大家都并不陌生,网上类似的 Sample 铺天盖地。为什么我还要针对 Android 运行时请求权限去做文章呢?那就是适配 Android O !
在 Android 官方文档中 https://developer.android.google.cn/preview/behavior-changes.html
对 Android O 的行为变更中的运行时请求权限做了修改。官方文档中强调:

【 Android 】Android Runtime Permissions 解决方案(适配 Android O)_第2张图片
权限.png

说白了就是对权限申请更加严格。针对这一变更,作为开发者既要兼顾 Android M - Android N 的运行时请求权限,也要适配 Android O 的运行时请求权限。

好的,跟着我的思路开始我们的运行时请求权限解决方案。

先看效果图:
① 在没授予权限之前,所有需要的权限都是未选中状态。


【 Android 】Android Runtime Permissions 解决方案(适配 Android O)_第3张图片
1.png

② Android 运行时请求权限。


2.gif

③ 权限授予之后,所有需要的权限都是选中状态。
【 Android 】Android Runtime Permissions 解决方案(适配 Android O)_第4张图片
3.png

看过示例图之后,我们先来了解一下哪些权限需要在运行时请求。

根据官方 API 文档 https://developer.android.google.cn/guide/topics/security/permissions.html 中所描述的内容,我们可以归纳出以下几点需要注意的地方:

  1. 如果设备运行的是 Android 6.0(API 级别 23)或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本,则应用在运行时向用户请求权限。用户可随时调用权限,因此应用在每次运行时均需检查自身是否具备所需的权限。

  2. 如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在用户安装应用时要求用户授予权限。如果将新权限添加到更新的应用版本,系统会在用户更新应用时要求授予该权限。用户一旦安装应用,他们撤销权限的唯一方式是卸载应用。

  3. 可能在程序运行期间的多个位置实施特定权限:
    ① 在调用系统时,防止应用执行某些功能。
    ② 在启动 Activity 时,防止应用启动其他应用的 Activity。
    ③ 在发送和接收广播时,控制谁可以接收您的广播,谁可以向您发送广播。
    ④ 在访问和操作内容提供程序时。
    ⑤ 绑定至服务或启动服务。

  4. 正常权限和危险权限
    ① 正常权限涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。例如,设置时区的权限就是正常权限。如果应用声明其需要正常权限,系统会自动向应用授予该权限。
    ② 危险权限涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如,能够读取用户的联系人属于危险权限。如果应用声明其需要危险权限,则用户必须明确向应用授予该权限。

  5. 危险权限和权限组
    分成 9 大类:

CALENDAR                 READ_CALENDAR
                            WRITE_CALENDAR
CAMERA                   CAMERA
CONTACTS                 READ_CONTACTS
                            WRITE_CONTACTS
                            GET_ACCOUNTS
LOCATION                 ACCESS_FINE_LOCATION
                            ACCESS_COARSE_LOCATION 
MICROPHONE               RECORD_AUDIO
PHONE                    READ_PHONE_STATE
                            CALL_PHONE
                            READ_CALL_LOG
                            WRITE_CALL_LOG
                            ADD_VOICEMAIL
                            USE_SIP
                            PROCESS_OUTGOING_CALLS
SENSORS                  BODY_SENSORS
SMS                      SEND_SMS 
                            RECEIVE_SMS 
                            READ_SMS 
                            RECEIVE_WAP_PUSH 
                            RECEIVE_MMS 
STORAGE                  READ_EXTERNAL_STORAGE 
                            WRITE_EXTERNAL_STORAGE

了解了哪些权限属于危险权限之后,跟随我一起进行 Android Runtime Permissions 解决方案。

(这里以 CALENDAR 为例,其他大同小异,获取完整代码请拖拽至文章末尾处。)

  1. 定义请求权限
private static final int REQUEST_CALENDAR = 0;
  1. 考虑到 Android O 之后的权限请求,故将同一类的都放在一个数组里进行处理
private static String[] PERMISSION_CALENDAR = {
        Manifest.permission.READ_CALENDAR,
        Manifest.permission.WRITE_CALENDAR
};
  1. 做权限请求的回调
public void showCalendar(View viewShowCalendar) {
    Log.i(TAG, "Show calendar button pressed. Checking permissions.");

    // Verify that all required contact permissions have been granted.
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR)
            != PackageManager.PERMISSION_GRANTED
            || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CALENDAR)
            != PackageManager.PERMISSION_GRANTED) {
        // Calendar permissions have not been granted.
        Log.i(TAG, "Calendar permissions has not been granted. Requesting permissions.");
        requestCalendarPermissions();
    } else {
        // Calendar permissions have been granted. Show the contacts fragment.
        Log.i(TAG,
                "Calendar permissions have already been granted. Displaying calendar details.");
        showCalendarDetails();
    }
}

这里就要做分支处理:
① 没有给予请求所需的权限,就要发出权限请求 requestCalendarPermissions()
② 如果已授予权限,那么就执行你想要执行的动作 showCalendarDetails()

  1. 发出权限请求
private void requestCalendarPermissions() {
    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.READ_CALENDAR)
            || ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.WRITE_CALENDAR)) {

        // Provide an additional rationale to the user if the permission was not granted
        // and the user would benefit from additional context for the use of the permission.
        // For example, if the request has been denied previously.
        Log.i(TAG, "Displaying calendar permission rationale to provide additional context.");

        // Display a SnackBar with an explanation and a button to trigger the request.
        Snackbar.make(mLayout, R.string.permission_calendar_rationale,
                Snackbar.LENGTH_INDEFINITE)
                .setAction(R.string.ok, new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        ActivityCompat
                                .requestPermissions(MainActivity.this, PERMISSION_CALENDAR,
                                        REQUEST_CALENDAR);
                    }
                })
                .show();
    } else {
        // Calendar permissions have not been granted yet. Request them directly.
        ActivityCompat.requestPermissions(this, PERMISSION_CALENDAR, REQUEST_CALENDAR);
    }
}
  1. 当权限已被授于,自定义执行动作
private void showCalendarDetails() {
    getSupportFragmentManager().beginTransaction()
            .replace(R.id.content_fragment, CalendarFragment.newInstance())
            .addToBackStack("calendar")
            .commit();
}

✱✱✱ 重中之重 ✱✱✱ 当权限请求完成之后回调。重写 onRequestPermissionsResult()

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {

    if (requestCode == REQUEST_CALENDAR) {
        Log.i(TAG, "Received response for calendar permissions request.");

        // We have requested multiple permissions for calendar, so all of them need to be
        // checked.
        if (PermissionUtil.verifyPermissions(grantResults)) {
            // All required permissions have been granted, display contacts fragment.
            Snackbar.make(mLayout, R.string.permission_available_calendar,
                    Snackbar.LENGTH_SHORT)
                    .show();
        } else {
            Log.i(TAG, "Calendar permissions were not granted.");
            Snackbar.make(mLayout, R.string.permissions_not_granted, Snackbar.LENGTH_SHORT)
                    .show();
        }
    }
    
    ...

}



最后在 AndroidManifest 里面申请需要的权限




    
    

    ...


项目代码已上传至 GitHub https://github.com/cnwutianhao/AndroidRuntimePermissions
欢迎 Star 、Fork 。如有遗漏或错误请指摘。

项目已在以下机器上进行完整测试
Nexus 5 Android 6.0.1 (真机)
Nexus 5x Android O (模拟器)

你可能感兴趣的:(【 Android 】Android Runtime Permissions 解决方案(适配 Android O))