Android8.0也就是Android O即将要发布了,有不少新特性,目前咱们能够经过AndroidStudio3.0 Canary版本下载Android O最新的系统映像的Developer Preview 4版本,Developer Preview 4是Android O正式版推出前的最后一个预览版本,因此它是Android O的候选版本,咱们可使用它来完成开发和测试,让咱们的应用平稳过分到Android O。html
后期会计划出一篇Android O行为变化和兼容方案的文章,本篇文章主要讲Android O行为变化的其中一点——系统运行时权限的策略变化和适配方案。java
Android系统的运行时权限是从Android 6.0(Android M)开始加入的,若是你还不知道Android运行时权限,你能够看我在掘金的另外一篇文章Android 6.0 运行时权限管理最佳实践:
juejin.im/post/57d5de… android
针对运行时权限管理,有不少开源的管理库,去年这个时候本人也开源了一个运行权限管理方案,它最大程度上兼容了国产机,固然也兼容了Android 8.0:
github.com/yanzhenjie/… git
在正式开始以前,先纠正一个问题,在网上看到有项目能够作到自定义申请受权的系统Dialog,首先要纠正就目前来看是绝对不行的,最多在调用申请的代码以前弹一个本身的Dialog提示用户要申请受权了。我快速拜读了下那个项目源码,果真如我想象的同样,在绕了一个圈子后最终仍是调用了系统申请受权的代码。github
若是你喜欢看Google官网的文章,你能够看这里:
developer.android.com/preview/beh…数组
在 Android O 以前,若是应用在运行时请求权限而且被授予该权限,系统会错误地将属于同一权限组而且在清单中注册的其余权限也一块儿授予应用。ide
对于针对Android O的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而一旦用户为应用授予某个权限,则全部后续对该权限组中权限的请求都将被自动批准。post
例如,假设某个应用在其清单中列出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
为例来具体分析一下,这对咱们现有的代码有什么影响。ui
正式开始以前,咱们先约定两个方法:
/** * 拿到没有被受权的权限。 */
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的运行时权限策略的特色,为了适配各个版本的系统,咱们的代码会变成以下方式(伪代码):
// 须要申请的权限。
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的权限策略的变化了。
这里只是介绍下AndPermisison
也兼容了Android O的权限变化,若是你以为这个项目不适合你,你能够自行封装一个,我比较鼓励开发者本身动手,下面是开源地址:
github.com/yanzhenjie/…
它的一些简单的特色:
申请多个权限组示例:
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();复制代码
申请单个或者某几个权限示例,由于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的运行时权限策略变化和应对方案的介绍到这里就结束了,若是还不理解的能够在文章下方留言。