【 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()

@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 apk 获得系统权限
  2. Android抛出异常NetworkOnMainThreadException解决方案
  3. android 自定义 permission 权限
  4. Received status code 400 from server: Bad Request解决方案
  5. Android横竖屏切换解决方案
  6. Android预制APP第一次打开时不弹权限提示页面
  7. Android 常用的权限
  8. 关于Android启动页全屏的解决方案
  9. Android的权限permission

随机推荐

  1. 北大国发院SSCI期刊等级分区什么鬼?
  2. mongodb大数据分页
  3. Spark On HBase
  4. 云数据库HBase企业级安全解析
  5. 重构单体为微服务
  6. 全面的区域科学研究数据获取途径汇总
  7. 0331作业
  8. Clad还是Tobit, 归并最小绝对偏差, 做Tob
  9. 我不是药神,我是印度仿制药,但我拯救了中
  10. CMS前世今生