Android8.0运行时权限策略变化和适配方案
版权声明:转载必须注明本文转自严振杰的博客: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
本文中申请权限的示例代码是原生代码,没有使用AndPermission。
Android O的运行时权限策略变化
如果你喜欢看Google官网的文章,你可以看这里:
https://developer.android.com/preview/behavior-changes.html#rmp
在 Android O 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。
对于针对Android O的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。
例如,假设某个应用在其清单中列出READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE。应用请求READ_EXTERNAL_STORAGE,并且用户授予了该权限,如果该应用针对的是API级别24或更低级别,系统还会同时授予WRITE_EXTERNAL_STORAGE,因为该权限也属于STORAGE权限组并且也在清单中注册过。如果该应用针对的是Android O,则系统此时仅会授予READ_EXTERNAL_STORAGE,不过在该应用以后申请WRITE_EXTERNAL_STORAGE权限时,系统会立即授予该权限,而不会提示用户。
下面我们还是以READ_EXTERNAL_STORAGE和WRITE_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 (www.wmyl15.com/ 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[www.wmyl11.com/] LOCATION; // 读位置信息。
public static final String[www.xucaizxyl.com/] MICROPHONE; // 使用麦克风。
public static final String[www.lieqibiji.com/] PHONE; // 读电话状态、打电话、读写电话记录。
public static final String[www.dejiaylsmile.cn] SENSORS; // 传感器。
public static final String[www.yongshiyule178.com] SMS; // 读写短信、收发短信。
public static final String[www.xingchexiu.com] 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,
在Android M以前使用某权限是不需要用户授权的,只要在Manifest中注册即可,在Android M之后需要注册并申请用户授权,所以我们根据系统版本在Android M以前用一个空数组作为权限组,在Android M以后用真实数组权限。
因为要传入多个权限组,所以我们约定的两个方法就不够用了,所以我们加两个方法:
/**
* 拿到没有被授权的权限。
*/
String[] getDeinedPermission(String... permissions);
/**
* 请求几个权限。
*/
void requestPermission(String... deinedPermissions);
/**
* 拿到没有被授权的权限。
*/
String[] getDeinedPermission(String[]... permissions);
/**
* 请求几个权限。
*/
void requestPermission(String[]... www.senta77.com/ deinedPermissions);
于是我们申请权限的代码就简化成这样了:
// 这方法里面判断版本,返回空数组或者没有权限的数组。
String[] deniedPermissions = getDeinedPermission(Permission.STORAGE, Permission.SMS);
if(deniedPermissions.length <= 0) {
// TODO do something...
} else {
requestPermission(deniedPermissions, callback);
当然这不是最简化的,但是已经足以兼容到Android O的权限策略的变化了。
如果是AndPermission如何做到最简
这里只是介绍下AndPermisison也兼容了Android O的权限变化,如果你觉得这个项目不适合你,你可以自行封装一个,我比较鼓励开发者自己动手,下面是开源地址:
https://github.com/yanzhenjie/AndPermission
它的一些简单的特点:
1. 链式调用,一句话申请权限,省去复杂的逻辑判断。
2. 支持注解回调结果、支持Listener回调结果。
3. 拒绝一次某权限后,再次申请该权限时可使用Rationale向用户说明申请该权限的目的,在用户同意后再继续申请,避免用户勾选不再提示而导致不能再次申请该权限。
4. 就算用户拒绝权限并勾选不再提示,可使用SettingDialog提示用户去设置中授权。
5. RationaleDialog和SettingDialog允许开发者自定义。
6. AndPermission自带默认对话框除可自定义外,也支持国际化。
7. 支持在任何地方申请权限,不仅限于Activity和Fragment等。
8. 支持申请权限组、兼容Android8.0。
申请多个权限组示例:
AndPermission.with(this)
.permission(Permission.CAMERA, Permission.SMS) // 多个权限组。
.callback(new PermissionListener() {
@Override
public void onSucceed(int i, @NonNull List<String> list) {
// TODO do something...
}
@Override
public void onFailed(int i, @NonNull List<String> list) {
// TODO 用户没有同意授权,一般弹出Dialog让用户去Setting中授权。
}
})
.start();
14
申请单个或者某几个权限示例,因为Android O的出现,现在不鼓励这样使用了,但是在Android O正式发布前没有问题:
AndPermission.with(this)
.permission(
// 多个不同权限组权限,现在不鼓励这样使用了,但是在Android O正式发布前没有问题。
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_SMS
)
.callback(new PermissionListener() {
@Override
public void onSucceed(int i, @NonNull List<String> list) {
// TODO do something...
}
@Override
public void onFailed(int i, @NonNull List<String> list) {
// TODO 用户没有同意授权,一般弹出Dialog让用户去Setting中授权。
}
})
.start();
关于Android O的运行时权限策略变化和应对方案的介绍到这里就结束了,如果还不理解的可以在博客下方留言。
版权声明:转载必须注明本文转自严振杰的博客:http://www.6788878.cn/
Android8.0运行时权限策略变化和适配方案的更多相关文章
- Android 6.0 运行时权限处理完全解析
一.概述 随着Android 6.0发布以及普及,我们开发者所要应对的主要就是新版本SDK带来的一些变化,首先关注的就是权限机制的变化.对于6.0的几个主要的变化,查看查看官网的这篇文章http:// ...
- Android 6.0+ 运行时权限
1.权限被分为了普通和危险两种 2.打电话的Demo import android.Manifest; import android.app.Activity; import android.cont ...
- Android 6.0 运行时权限处理完全解析 (摘抄)
转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/50709663: 本文出自:[张鸿洋的博客] 一.概述 随着Android 6. ...
- Android权限管理之Android 6.0运行时权限及解决办法
前言: 今天还是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在Android 6.0以 ...
- Android6.0运行时权限(基于RxPermission开源库)
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 在6.0以前的系统,都是权限一刀切的处理方式,只要用户安装,Manifest申请的权限都会被赋予,并且安装后权限也撤销不了. And ...
- Android数据存储之Android 6.0运行时权限下文件存储的思考
前言: 在我们做App开发的过程中基本上都会用到文件存储,所以文件存储对于我们来说是相当熟悉了,不过自从Android 6.0发布之后,基于运行时权限机制访问外置sdcard是需要动态申请权限,所以以 ...
- 谈谈Android 6.0运行时权限理解
前言 谷歌在2015年8月份时候,发布了Android 6.0版本,代号叫做“棉花糖”(Marshmallow ),其中的很大的一部分变化,是在用户权限授权上,或许是感觉之前默认授权的不合理,现在6. ...
- android-详解Android 6.0运行时权限
感谢郭神,从Android 6.0开始,不再是安装应用时用户确定获得全部的权限.而是在使用软件过程中需要该权限时,弹出对话框让用户选择权限.不仅如此,用户选择权限后还可以关闭. 检查是否获得权限 通过 ...
- Android 6.0运行时权限
一.Runtime Permissions Android 6.0在手机安全方面做的一个处理就是增加了运行时权限(Runtime Permissions). 新的权限机制更好的保护了用户的隐私,Goo ...
随机推荐
- linux应用编程之进程间同步
一.描述 在操作系统中,异步并发执行环境下的一组进程,因为相互制约关系,进而互相发送消息.互相合作.互相等待,使得各进程按一定的顺序和速度执行,称为进程间的同步.具有同步关系的一组并发进程,称为合作进 ...
- 使用Topshelf管理Windows服务
目的:以控制台方式开发Windows服务程序,调试部署方便. https://www.cnblogs.com/itjeff/p/8316244.html https://www.cnblogs.com ...
- 【下一代核心技术DevOps】:(四)私有镜像库阿里云Docker服务使用
1.使用阿里云镜像库有很多优点 稳定可靠,阿里技术,放心使用. 国内cdn多节点加速,下载速度非常快 可以和阿里云Git代码集成,不需要第三方CI工具,当然带的自动构建服务也可以和其他的Git库集成, ...
- 【nodejs】让nodejs像后端mvc框架(asp.net mvc)一样处理请求--控制器和处理函数的注册篇(4/8)【controller+action】
文章目录 前情概要 前边的文章把一些基本的前置任务都完成了.接下就是比较重要的处理函数action是如何自动发现和注册的拉,也就是入口函数RouteHandler(也是我们的第一个express中间件 ...
- Centos6.9下安装并使用VNC的操作记录
VNC是一个的"远程桌面"工具.,通常用于“图形界面”的方式登录服务器,可视化操作.废话不多说了,操作记录如下: 1)安装桌面环境 [root@vm01 ~]# yum -y gr ...
- Java多线程的使用以及原理
Java有两种方式实现多线程. 第一种——继承Thread类,并重写run方法 步骤: 定义类继承Thread类: 重写子类的run方法,将线程需要执行的代码写在run方法中: 创建子类的对象,则创建 ...
- 《Linux内核分析》读书笔记(四章)
<Linux内核分析>读书笔记(四章) 标签(空格分隔): 20135328陈都 第四章 进程调度 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间,进程调度程序可看做在可运行 ...
- servlet请求转发
来源:http://www.2cto.com/kf/201610/554591.html 请求转发:Servlet(源组件)先对客户请求做一些预处理操作(数据处理),然后把请求转发给其他Web组件(目 ...
- VC6到VC2010,项目迁移错误
错误信息: error C2440: “static_cast”: cannot from “BOOL (__thiscall CSelectRect::* )(void)” to “BOOL (__ ...
- Delphi/XE2 使用TIdHttp控件下载Https协议服务器文件[转]
之前的一篇博文详细描述了使用TIdhttp控件下载http协议的文件,在我项目的使用过程中发现对于下载Https协议中的文件与Http协议的文件不同,毕竟Https在HTTP协议基础上增加了SSL协议 ...