Android 动态权限管理
最新
Android 6.0(Marshmallow, 软棉花糖,Api 23),权限分为普通权限和许可权限,许可权限分类归组,该组一个权限被许可后,其他的权限均可使用。
基本介绍
- 普通权限 - 只需在xml申请即可,使用方法和6.0之前的一样。应用安装后会默认获得许可。 
- 许可权限 - 具体权限分组情况可以使用以下shell命令查看 - 1 - adb shell pm list permissions -d -g - 同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_CONTACTS被授权了,app也有READ_CONTACTS和GET_ACCOUNTS了 
- 相关方法 - ContextCompat.checkSelfPermission() - 检查应用是否拥有该权限,若已授权,返回值为PERMISSION_GRANTED,否则返回PERMISSION_DENIED。 
- ActivityCompat.requestPermissions() - 该方法在M之前版本调用,OnRequestPermissionsResultCallback 直接被调用,带着正确的 PERMISSION_GRANTED或者 PERMISSION_DENIED。 
- AppCompatActivity.onRequestPermissionsResult() - 该方法类似于Activity的OnActivityResult()的回调方法,主要接收请求授权的返回值。 
 
- 需运行时动态申请的权限组 - 日历 - android.permission-group.CALENDAR- 相机 - android.permission-group.CAMERA- 联系人 - android.permission-group.CONTACTS- 定位 - android.permission-group.LOCATION- 耳机 - android.permission-group.MICROPHONE- 电话 - android.permission-group.PHONE- 传感器 - android.permission-group.SENSORS- 短信 - android.permission-group.SMS- 存储 - android.permission-group.STORAGE
权限适配解决方案
基本方案(原生方法)
原生方案就是采用android系统提供的权限相关的方法来动态的做权限适配处理,是其他解决方案的基础
我这里来测试读取联系人和打电话这两个6.0之后要在运行时赋予的权限。
MainActivity.java文件内容如下:
| 1 | package com.rainmonth.myapplication; | 
activity_main.xml文件如下
| 1 | 
 | 
权限申请结果的处理主要在onRequestPermissionsResult中,而处理权限申请所用到的两个HashMap则是在requestPermissions就传递过去了。根据requestCode来在map中查找对应的Runnable对象来处理。
RxJava + RxPermissions方案
在了解了RxJava后,若果你看到上述代码就会觉得上面的基本方案肯定能有一种RxJava式的封装来解决(基本方案中其处理核心就是利用系统提供的onRequestPermissionsResult这个回调方法,RxJava可以很好的解决回调问题),没错,RxPermissions就应运而生了。
RxPermissions的源码还没时间具体分析,先看看它的使用方式。
- 引入 - 添加依赖 - 1 
 2
 3
 4
 5
 6
 7
 8
 9- // RxJava 版本小于2.0 
 dependencies {
 compile 'com.tbruyelle.rxpermissions:rxpermissions:0.9.0@aar'
 }
 // RxJava 版本2.0以上
 dependencies {
 compile 'com.tbruyelle.rxpermissions2:rxpermissions:0.8.2@aar'
 }
- 直接申请权限(以Manifest.permission.CAMERA为例) - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15- RxPermissions.getInstance(this) 
 .request(Manifest.permission.CAMERA)
 .subscribe(new Action1<Boolean>() {
 
 public void call(Boolean granted) {
 if (granted) { // 在android 6.0之前会默认返回true
 // 已经获取权限
 String jpgPath = getCacheDir() + "test.jpg";
 takePhotoByPath(jpgPath, 2);
 } else {
 // 未获取权限
 Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show();
 }
 }
 });
- 条件触发获取权限(可以结合RxBinding使用) - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16- RxView.clicks(findViewById(R.id.request_permission)) 
 .compose(RxPermissions.getInstance(this).ensure(Manifest.permission.CAMERA))
 .subscribe(new Action1<Boolean>() {
 
 public void call(Boolean granted) {
 if (granted) { // 在android 6.0之前会默认返回true
 // 已经获取权限
 String jpgPath = getCacheDir() + "test.jpg";
 takePhotoByPath(jpgPath, 2);
 } else {
 // 未获取权限
 Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show();
 }
 }
 });
- 同时请求多个权限(合并结果) - 以同时申请拍照和录音权限,代码如下: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14- RxPermissions.getInstance(MainActivity.this).request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) 
 .subscribe(new Action1<Boolean>() {
 
 public void call(Boolean granted) {
 if (granted) { // 在android 6.0之前会默认返回true
 // 已经获取权限
 String jpgPath = getCacheDir() + "test.jpg";
 takePhotoByPath(jpgPath, 2);
 } else {
 // 未获取权限
 Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show();
 }
 }
 });- 上面这种请求权限方式,只有所有的权限请求都同意之后,才会返回true,有一个为false,则返回false 
- 同时获取多个权限(分别获取结果) - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19- RxPermissions.getInstance(MainActivity.this).requestEach(Manifest.permission.CAMERA, 
 Manifest.permission.RECORD_AUDIO)
 .subscribe(new Action1<Permission>() {
 
 public void call(Permission permission) {
 if (permission.name.equals(Manifest.permission.CAMERA)) {
 if (permission.granted) {
 String jpgPath = getCacheDir() + "test.jpg";
 takePhotoByPath(jpgPath, 2);
 } else {
 // 未获取权限
 Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show();
 }
 } else if (permission.name.equals(Manifest.permission.RECORD_AUDIO)) {
 }
 }
 });
RxJava的这种链式写法很好的应用在权限请求上了,哈哈!
其他问题
- 在部分手机Android 5.0上直接使用ContextWrapper的checkSelfPermission方法会抛出异常(NoSuchMethodException)这个时候记得将其替换成ContextCompat的checkSelfPermission方法
- API 23之前的机子调用checkSelfPermission的返回值一直是0,也就是PERMISSION.GRANTED(不管用户是否已经赋予该权限),解决方法就是调用support包下面的PermissionChecker的checkSelfPermission方法
其他与权限申请相关的项目
使用标注的方式,动态生成类处理运行时权限,目前还不支持嵌套Fragment。
简化运行时权限处理
Google官方的例子