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 ...
随机推荐
- ionic 访问odoo11之具体业务类api接口
在前面测试通过odoo登录的功能,这次的问题重点是如何访问后台具体的业务类的接口呢?这次就以我们在odoo中安装的lunch模块为例,目标是获取lunch.alert的数据,如下图 具体过程接上次文章 ...
- SQL跨服务器查询数据库
有时候一个项目需要用到两个数据库或多个数据库而且这些数据库在不同的服务器上时,就需要通过跨服务器查找数据 在A服务器的数据库a查询服务器B的数据库b 的bb表 假如服务器B的IP地址为:10.0.22 ...
- [Socket]Socket文件传输
1.Server import java.io.DataInputStream; import java.io.FileOutputStream; import java.io.IOException ...
- Bash : IO 重定向
标准输入/输出(standard I/O)可能是软件设计原则里最重要的概念了.这个概念就是:程序应该有数据的来源端.数据的目的端(输出结果的地方)已经报告问题的地方,它们分别被称为标准输入(stand ...
- 基于Ping和Telnet/NC的监控脚本案例分析
案例一:单纯地对某些ip进行ping监控 [root@test opt]# cat /opt/hosts_ip_list 192.168.10.10 192.168.10.11 192.168.10. ...
- 关于dreamweaver的软件测评
最近在用javascript编写程序,于是便用到了dreamweaver .所以,想写一个关于dreamweaver的软件测评. 学生本人使用的是dreamweaver 8.首先,谈谈本人使用感受,打 ...
- mac系统下修复第三方Python包bug
发现问题 今天在github上fork了CI 3.x的中文手册,按照README文档一步步进行Sphinx和相关工具的安装,最终build生成html版手册.操作到第6步执行`make html`的时 ...
- Scrum Meeting NO.10
Scrum Meeting No.10 1.会议内容 2.任务清单 徐越 序号 近期的任务 进行中 已完成 1 "我"回答过的问题 -- 界面 √ 2 "问题" ...
- 第二次sprint
一.这次冲刺就是实施阶段了,主要对程序进行一次骨架的构建,基本上已经完成了界面的设计,但是在算法上还有很大漏洞,整个程序还是属于不成熟阶段. 二.这是我们的界面 三.已经有的功能:随机生成题目并算出答 ...
- HDOJ2099_整数的尾数
一道我以为不会这么简单,然后暴力盲打竟然给过了的题. HDOJ2099_整数的尾数 #include<stdio.h> #include<stdlib.h> #include& ...