permissions.png

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

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

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

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


1.png

② Android 运行时请求权限。


2.gif
③ 权限授予之后,所有需要的权限都是选中状态。
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()

@Overridepublic 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 里面申请需要的权限

<?xml version="1.0" encoding="utf-8"?>            ...

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

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

更多相关文章

  1. Android学习之Android本地存储的五种方式
  2. Android体系结构
  3. android点滴(23)之android监听应用卸载
  4. 《宅男的android开发指南》(翻译)--1
  5. Android(安卓)Display System --- Surface Flinger
  6. 如何让android apk 获得系统权限
  7. Android(安卓)SDK 2.2 开发环境搭建
  8. 使用Eclipse+ADT开发android 1.5程序
  9. [新技术]新技术

随机推荐

  1. Go语言个人学习笔记(Pythonista)
  2. 【DB宝50】Oracle异构平台迁移之完全可传
  3. RocketMQ入门到入土(一)新手也能看懂的原理
  4. DEA 2021.1最新永久激活(免费激活至 2099
  5. RocketMQ入门到入土(五)消息持久化存储源码
  6. 炫酷,SpringBoot+Echarts实现用户访问地图
  7. RocketMQ入门到入土(四)producer生产消息源
  8. 从入门到入土(三)RocketMQ 怎么保证的消息
  9. Java集合面试题(2021最新版)
  10. 函数的返回值,参数