Android8.0运行时权限策略变化和适配方案

标签: Android-O 运行时权限 Android-M Android8
70747人阅读 评论(19) 收藏 举报
分类:

版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com

Android8.0也就是Android O即将要发布了,有很多新特性,目前我们可以通过AndroidStudio3.0 Canary版本下载Android O最新的系统映像的Developer Preview 4版本,Developer Preview 4是Android O正式版推出前的最后一个预览版本,所以它是Android O的候选版本,我们可以使用它来完成开发和测试,让我们的应用平稳过度到Android O。

后期会计划出一篇Android O行为变化和兼容方案的文章,本篇文章主要讲Android O行为变化的其中一点——系统运行时权限的策略变化和适配方案

Android系统的运行时权限是从Android 6.0(Android M)开始加入的,如果你还不知道Android运行时权限,你可以看Android 6.0 运行时权限管理最佳实践
http://blog.csdn.net/yanzhenjie1003/article/details/52503533

针对运行时权限管理,有很多开源的管理库,16年9月份时本人也开源了一个运行权限管理方案,它最大程度上兼容了国产机,当然也兼容了Android 8.0:
https://github.com/yanzhenjie/AndPermission

在正式开始之前,先纠正一个问题,在网上看到有项目可以做到自定义申请授权的系统Dialog,首先要纠正就目前来看是绝对不行的,最多在调用申请的代码之前弹一个自己的Dialog提示用户要申请授权了。我快速拜读了下那个项目源码,果然如我想象的一样,在绕了一个圈子后最终还是调用了系统申请授权的代码。


Android O的运行时权限策略变化

如果你喜欢看Google官网的文章,你可以看这里:
https://developer.android.com/preview/behavior-changes.html#rmp

在 Android O 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。

对于针对Android O的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。

例如,假设某个应用在其清单中列出READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE。应用请求READ_EXTERNAL_STORAGE,并且用户授予了该权限,如果该应用针对的是API级别24或更低级别,系统还会同时授予WRITE_EXTERNAL_STORAGE,因为该权限也属于STORAGE权限组并且也在清单中注册过。如果该应用针对的是Android O,则系统此时仅会授予READ_EXTERNAL_STORAGE,不过在该应用以后申请WRITE_EXTERNAL_STORAGE权限时,系统会立即授予该权限,而不会提示用户。(有人私下跟我讲这跟原来没区别,明眼人应该看得出来,本组没申请的权限的状态应该是拒绝的,那么直接使用拒绝状态的权限会怎么样呢。)

下面我们还是以READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE为例来具体分析一下,这对我们现有的代码有什么影响。

正式开始之前,我们先约定两个方法:

/**
 * 拿到没有被授权的权限。
 */
getDeinedPermission(String... permissions);
/**
 * 请求几个权限。
 */
requestPermission(String... deinedPermissions);

权限的常量在Manifest.permission类中,而READ_EXTERNAL_STORAGE权限是在API 16之后才添加的,所以在在Android M出来后为了适配更低版本的系统,我们一般是这样申请权限的(伪代码):

// 需要申请的权限。
String[] permissions = {
    Manifest.permission.WRITE_EXTERNAL_STORAGE,
    Manifest.permission.READ_SMS,
    ...
};

String[] deniedPermissions = getDeinedPermission(permissions);

if(deniedPermissions.length <= 0) {
    // TODO do something...
} else {
    requestPermission(deniedPermissions, callback);
}

逻辑非常简单清晰,其中的callback是申请权限的回调,这里我们申请了WRITE_EXTERNAL_STORAGE权限,在Android O之前,我们同时会得到READ_EXTERNAL_STORAGE权限,我们在其它地方涉及到读取存储卡的操作时只需要判断有WRITE_EXTERNAL_STORAGE权限就去读取了。

霸特,此时应用如果安装在Android O的系统中我们会发现,判断了有WRITE_EXTERNAL_STORAGE权限后去读取存储卡内容时应用崩溃了,原因就是我们没有申请READ_EXTERNAL_STORAGE权限。

对Android O运行时权限策略变化的应对方案

针对Android O的运行时权限策略的特点,为了适配各个版本的系统,我们的代码会变成如下方式(伪代码):

// 需要申请的权限。
String[] permissions = {
    Manifest.permission.WRITE_EXTERNAL_STORAGE,
    Manifest.permission.READ_EXTERNAL_STORAGE,
    Manifest.permission.READ_SMS,
    ...
};

String[] deniedPermissions = getDeinedPermission(permissions);

if(deniedPermissions.length <= 0) {
    // TODO do something...
} else {
    requestPermission(deniedPermissions, callback);
}

但是这样会存在两个问题,一是有的权限组权限比较多,开发者难易全部记住;二是READ_EXTERNAL_STORAGE这个权限常量是在API 16时才被添加到SDK中,类似这样的权限常量还有好几个,有的甚至在Android M时才被添加到SDK中。如果我们强制写了,当APP运行在低版本的系统中时,还是会崩溃。有人就说了,我们在申请之前判断系统版本不就好啦?当然,如果你不嫌麻烦,这是完全可以的。

升级方案

因此我们总结出一个更优的方案,归根结底就是申请权限时要申请权限组,而不是单一的某个权限。所以我们按照系统权限组分类,把一个组的常量放到一个数组中,并根据系统版本为这个数组赋值,于是乎产生了这样一个类:

public final class Permission {

    public static final String[] CALENDAR;   // 读写日历。
    public static final String[] CAMERA;     // 相机。
    public static final String[] CONTACTS;   // 读写联系人。
    public static final String[] LOCATION;   // 读位置信息。
    public static final String[] MICROPHONE; // 使用麦克风。
    public static final String[] PHONE;      // 读电话状态、打电话、读写电话记录。
    public static final String[] SENSORS;    // 传感器。
    public static final String[] SMS;        // 读写短信、收发短信。
    public static final String[] STORAGE;    // 读写存储卡。

    static {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            CALENDAR = new String[]{};
            CAMERA = new String[]{};
            CONTACTS = new String[]{};
            LOCATION = new String[]{};
            MICROPHONE = new String[]{};
            PHONE = new String[]{};
            SENSORS = new String[]{};
            SMS = new String[]{};
            STORAGE = new String[]{};
        } else {
            CALENDAR = new String[]{
                    Manifest.permission.READ_CALENDAR,
                    Manifest.permission.WRITE_CALENDAR};

            CAMERA = new String[]{
                    Manifest.permission.CAMERA};

            CONTACTS = new String[]{
                    Manifest.permission.READ_CONTACTS,
                    Manifest.permission.WRITE_CONTACTS,
                    Manifest.permission.GET_ACCOUNTS};

            LOCATION = new String[]{
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION};

            MICROPHONE = new String[]{
                    Manifest.permission.RECORD_AUDIO};

            PHONE = new String[]{
                    Manifest.permission.READ_PHONE_STATE,
                    Manifest.permission.CALL_PHONE,
                    Manifest.permission.READ_CALL_LOG,
                    Manifest.permission.WRITE_CALL_LOG,
                    Manifest.permission.USE_SIP,
                    Manifest.permission.PROCESS_OUTGOING_CALLS};

            SENSORS = new String[]{
                    Manifest.permission.BODY_SENSORS};

            SMS = new String[]{
                    Manifest.permission.SEND_SMS,
                    Manifest.permission.RECEIVE_SMS,
                    Manifest.permission.READ_SMS,
                    Manifest.permission.RECEIVE_WAP_PUSH,
                    Manifest.permission.RECEIVE_MMS};

            STORAGE = new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE};
        }
    }

}

在Android M以前使用某权限是不需要用户授权的,只要在Manifest中注册即可,在Android M之后需要注册并申请用户授权,所以我们根据系统版本在Android M以前用一个空数组作为权限组,在Android M以后用真实数组权限。

因为要传入多个权限组,所以我们约定的两个方法就不够用了,所以我们加两个方法:

/**
 * 拿到没有被授权的权限。
 */
String[] getDeinedPermission(String... permissions);
/**
 * 请求几个权限。
 */
void requestPermission(String... deinedPermissions);
/**
 * 拿到没有被授权的权限。
 */
String[] getDeinedPermission(String[]... permissions);
/**
 * 请求几个权限。
 */
void requestPermission(String[]... deinedPermissions);

于是我们申请权限的代码就简化成这样了:

// 这方法里面判断版本,返回空数组或者没有权限的数组。
String[] deniedPermissions = getDeinedPermission(Permission.STORAGE, Permission.SMS);

if(deniedPermissions.length <= 0) {
    // TODO do something...
} else {
    requestPermission(deniedPermissions, callback);
}

当然这不是最简化的,但是已经足以兼容到Android O的权限策略的变化了。

关于Android O的运行时权限策略变化和应对方案的介绍到这里就结束了,如果还不理解的可以在博客下方留言。


版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com

查看评论

公司项目Android8.0适配分析

两个官方链接: 1. 官方推荐的兼容测试方式 2. Android8.0行为变更说明 Android 8.0 行为变更包括两个部分:针对所有 API 级别的应用和针对 Android...
  • xwh_1230
  • xwh_1230
  • 2017-11-03 17:30:51
  • 4875

android 适配android 8.0时遇到的问题

前几天某应用平台通知我,说我没有适配android O,让我适配之后再进行上传更新. 虽说android 8.0出了一段时间了,但毕竟常用的机型中没有,所以没有适配.也是个人漏洞 然后在Pixel X...
  • hh_tom
  • hh_tom
  • 2017-10-13 11:01:13
  • 3446

Android 2分钟刷Android 8.0系统 和 8.0适配 完美方案

本文将从以下3个方面详细介绍Anroid 8.0完美适配 1).快速刷Anroid 8.0系统 2).应用API的主要修改,源代码详解,值得注意的地方 3).Anroid O新特性,行为变更 ...
  • WHB20081815
  • WHB20081815
  • 2017-07-21 18:09:53
  • 16788

Android O 迁移(适配Android 8.0)

Android O Migrating 小伙伴们现在装载android 8.0 的手机已经在市面销售那么作为程序员的我们是否已经完成面向Android O的迁移了呢? 现在我们介绍下 And...
  • blueZhangFun
  • blueZhangFun
  • 2018-01-08 18:33:54
  • 946

Android 8.0新特性适配测试报告来啦!

WeTest 导读谷歌2017 I/O开发者大会上发布了Android 8.0的正式版, 其官方代号为Oreo(奥利奥)。网上关于Android8.0新功能特性的介绍已铺天盖地,新功能特性会对程序应用...
  • tantion
  • tantion
  • 2017-10-25 15:43:41
  • 1534

Android8.0 WiFi热点适配

在Android8.0上用以前的方式调试WiFi热点的时候发现无法正常开启热点,于是查了下,发现之前的热点打开接口已经废弃。原先的接口WifiManager.java中setWifiApEnabled...
  • bukker
  • bukker
  • 2017-11-27 21:45:07
  • 3416

Android O(8.0)通知栏适配

Android O 适配、NotificationChannel
  • rentee
  • rentee
  • 2017-10-21 14:48:07
  • 14733

值得你关注的Android8.0(Android O)上的重要变化

刚适配完Android7.0还没多久,就看到Android8.0(Android O)已经推出开发者预览版的新闻,我的心情你是可以想到的。这次趁早刷到最新版,运行示例代码,看看Google又做了哪些新...
  • w7849516230
  • w7849516230
  • 2017-04-01 09:31:56
  • 28939

Android O 8.0 运行时权限适配方案

一.序 在之前的文章聊聊Android M 6.0 的运行时权限曾提到过,Google Play开发者明年势必要努力将targetsdkversion升级到26的过程中。官网的文档:向 Androi...
  • jamin0107
  • jamin0107
  • 2018-01-05 18:12:49
  • 1075
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 170万+
    积分: 6714
    排名: 4404
    推荐
    欢迎关注我的公众号
    博客专栏