Android6.0如何进行权限适配?
本篇仅讨论如何使用,至于原理请看《Android6.0权限管理原理》
背景
在Android6.0之前Android的权限都是在安装的时候授予的,6.0之后,为了简化安装流程,并且方便用户控制权限,改成在使用时动态申请。当设置targetSdkVersion为23时,运行在Android6.0+的手机,就会调用6.0相关的API,达到动态控制权限的目的,但是如果没有在代码层面对于Android6.0权限
什么权限需要动态适配?
Android将权限分为两种:
- 普通权限
- 敏感(危险)权限
普通权限
这类权限不会泄露用户隐私,同时也不会导致手机安全问题,比如网络请求权限、WIFI状态等。
- ACCESS_NETWORK_STATE
- BLUETOOTH
- ACCESS_WIFI_STATE
敏感权限
这类权限与普通权限对应,可能会影响用户的隐私,存储数据等,比如拍照、存储、通讯录、地理GPS等,这类权限需要在Manifest中列出来。
- CALENDAR
- CAMERA
- CONTACTS
- LOCATION
- PHONE
特殊权限
例如系统弹窗等等
- SYSTEM_ALERT_WINDOW
- WRITE_SETTINGS
怎么动态适配权限?
对于敏感权限有一个原则,每次需要权限时都要检查,因为权限可能随时被回收,但是APP不一定知道,所以每次都需要检查,一旦没有,就需要根据返回结果处理后续逻辑。
实现步骤
在Manifest中列出来
不管是什么权限,都需要在Manifest中列出来,这样同时也是对6.0之前版本的一种兼容。
例如:
1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
需要时,显示的请求
在权限没被授予前提下,系统会显示授权对话框,让用户操作,目前授权对话框不可定制,不过可以在申请之前添加一些解释,告诉用户为什么需要该权限,但是Google提醒,不要做过多的解释,可能会使用户感到厌烦。
1 | 。。 |
处理授权回调
- 兼容6.0之前的处理:在6.0之前只存在install权限,一旦安装,所有权限都是授予,只需要处理授权成功即可。
- 需要对6.0的授权成功、失败、永不询问做处理。
具体做法如下:
1 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { |
如何在App中对于权限进行处理?
- 简单的封装回调
- 2.基于APT,采用注解方式简化编码逻辑,自动封装回调
采用最简单也是最直接的回调
1 | public class BasePermissionCompatActivity extends AppCompatActivity { |
在用的时候,根据结果处理后续的逻辑请求即可
1 | requestPermissions(activity, P_CAMERA, new OnGrantedListener() { |
基于APT注解,在编译过程中生成代码,自动添加回调
- 基于APT,定义一系列的Annotation,并动态生成辅助Listener类
- 添加Android支持库,在基类统一处理回调
- 添加工具类,连接绑定Listener与Activity(fragment)
APT生成支持库
1 | PermissionProcessor.java |
部分参考代码:
1 | @AutoService(Processor.class) |
Android支持库
1 | * BasePermissionCompatActivity.java |
参考代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31public class PermissionCompat {
private static int sNextRequestCode;
static final Map<Class<?>, OnGrantedListener> BINDERS = new LinkedHashMap<>();
// 分批次请求权限
public static void requestPermission(BasePermissionCompatActivity target, String[] permissions) {
Class<?> targetClass = target.getClass();
try {
// 找到监听Listener类,并实例一个
OnGrantedListener<BasePermissionCompatActivity> listener = findOnGrantedListenerForClass(targetClass, permissions);
if (PermissionUtils.hasSelfPermissions(target, permissions)) {
listener.onGranted(target, permissions);
} else if (PermissionUtils.shouldShowRequestPermissionRationale(target, permissions)) {
// 拒绝过,再次请求的时候,这个函数是否有必要,不在询问后,返回false,第一次返回false,
//listener.onShowRationale(target, permissions);
startRequest(target, listener, permissions);
} else {
startRequest(target, listener, permissions);
}
} catch (Exception e) {
throw new RuntimeException("Unable to bind views for " + targetClass.getName(), e);
}
}
private static void startRequest(BasePermissionCompatActivity target, OnGrantedListener listener, final @NonNull String[] permissions) {
target.setOnGrantedListener(listener);
ActivityCompat.requestPermissions(target, permissions, getNextRequestCode());
}
使用
- 1、Activity继承BasePermissionCompatActivity
- 2、用注解写回调函数,支持权限分组,跟单独处理,但是每个分组都要写自己的回调函数(目前回调函数,不支持参数)
- 3、回调必需配套,也就是一个权限必须对应四个函数,否则编译不通过
- 4、请求的权限必须有回调函数,不然报运行时错误–崩溃
1 | @ActivityPermission |