首页 > 代码库 > Android6.0运行时权限(基于RxPermission开源库)
Android6.0运行时权限(基于RxPermission开源库)
版权声明:本文为博主原创文章,未经博主允许不得转载。
前言
在6.0以前的系统,都是权限一刀切的处理方式,只要用户安装,Manifest申请的权限都会被赋予,并且安装后权限也撤销不了。
Android 6.0 采用新的权限模型,只有在需要权限的时候,才告知用户是否授权;是在runtime时候授权,而不是在原来安装的时候 ,同时默认情况下每次在运行时打开页面时候,需要先检查是否有所需要的权限申请。
判断是否是需要运行时权限的标记就是targetSDKVersion。
当targetSDKVersion<23的时候,仅在安装时赋予权限,使用时将不被提醒;
当targetSDKVersion≥23的时候才会使用新的运行时权限规则。
运行时权限未适配可能会导致应用崩溃或者在SD卡中创建目录和文件不成功。
解决方案:
一、将targetSDKVersion人为地降到小于23,这样就变成了还是默认使用权限,但是这种并不是Google所推荐使用的。
二、实现APP支持运行时权限
那么,哪些权限属于运行时权限呢?
权限的分组
Android中有很多权限,但并非所有的权限都是敏感权限,于是6.0系统就对权限进行了分类,一般为下述几类
- 正常(Normal Protection)权限
- 危险(Dangerous)权限
- 特殊(Particular)权限
- 其他权限(一般很少用到)
正常权限
正常权限具有如下的几个特点
- 对用户隐私没有较大影响或者不会带来安全问题。
- 安装后就赋予这些权限,不需要显示提醒用户,用户也不能取消这些权限。
上述的权限基本设计的是关于网络,蓝牙,时区,快捷方式等方面,只要在AndroidManifest.xml指定了这些权限,就会被授予,并且不能撤销。
注意:直接在AndroidManifest.xml文件中声明权限即可。
特殊权限
这里讲特殊权限提前讲一下,因为这个相对来说简单一些。
特殊权限,顾名思义,就是一些特别敏感的权限,在Android系统中,主要由两个
- SYSTEM_ALERT_WINDOW(设置悬浮窗,进行一些黑科技)
- WRITE_SETTINGS (修改系统设置)
关于上面两个特殊权限的授权,做法是使用startActivityForResult启动授权界面来完成。
注意:关于这两个特殊权限,一般不建议应用申请。
危险权限
危险权限实际上才是运行时权限主要处理的对象,这些权限可能引起隐私问题或者影响其他程序运行。
Android中的危险权限可以归为以下几个分组:
权限组 | 权限列表 |
android.permission-group.CALENDAR | android.permission.READ_CALENDAR (允许程序读取用户的日程信息) |
android.permission-group.CAMERA | android.permission.CAMERA (允许访问摄像头进行拍照) |
android.permission-group.CONTACTS | android.permission.READ_CONTACTS (允许应用访问联系人通讯录信息) android.permission.WRITE_CONTACTS (写入联系人,但不可读取) android.permission.GET_ACCOUNTS (访问GMail账户列表) |
android.permission-group.LOCATION | android.permission.ACCESS_COARSE_LOCATION (通过WiFi或移动基站的方式获取用户错略的经纬度信息,定位精度大概误差在30~1500米) android.permission.ACCESS_FINE_LOCATION (通过GPS芯片接收卫星的定位信息,定位精度达10米以内) |
android.permission-group.MICROPHONE | android.permission.RECORD_AUDIO (录制声音通过手机或耳机的麦克) |
android.permission-group.PHONE | android.permission.READ_PHONE_STATE (访问电话状态) android.permission.CALL_PHONE (允许程序从非系统拨号器里输入电话号码) android.permission.READ_CALL_LOG (允许应用程序读取用户的通话记录) android.permission.WRITE_CALL_LOG (允许一个程序写入(但不读取)用户的通话记录资料) com.android.voicemail.permission.ADD_VOICEMAIL (允许应用程序添加语音邮件进入系统) android.permission.USE_SIP (允许程序使用SIP视频服务) android.permission.PROCESS_OUTGOING_CALLS (允许程序监视,修改或放弃播出电话) |
android.permission-group.SENSORS | android.permission.BODY_SENSORS (允许从传感器,用户使用来衡量什么是他/她的身体内发生的事情,如心脏速率访问数据的应用程序) |
android.permission-group.SMS | android.permission.SEND_SMS (发送短信) android.permission.RECEIVE_SMS (接收短信) android.permission.READ_SMS (读取短信内容) android.permission.RECEIVE_WAP_PUSH (接收WAP PUSH信息) android.permission.RECEIVE_MMS (接收彩信) android.permission.READ_CELL_BROADCASTS () |
android.permission-group.STORAGE | android.permission.READ_EXTERNAL_STORAGE (允许程序读取外部存储,如SD卡读文件) android.permission.WRITE_EXTERNAL_STORAGE (允许程序写入外部存储,如SD卡上写文件) |
效果图
代码分析
- 基于Android Studio开发环境。
- 基于RxPermission开源库。
- 项目的最低sdk版本号(minSdkVersion)必须>=11。
- 分为以下四种情况分析:
A) 只有一个运行时权限申请的情况
B) 同时请求多个权限(合并结果)的情况
C) 同时请求多个权限(分别获取结果)的情况
D) 条件触发获取权限(结合RxBinding使用)的情况
使用步骤
一、检查minSdkVersion值
当前项目的app/ build.gradle文件中的minSdkVersion值必须>=11
二、添加依赖开源库
在当前项目的app/ build.gradle文件中的dependencies{}里面添加以下代码:
普通情况【这个暂时不会用】
//运行时权限compile ‘com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar‘
项目中使用了RxJava2的的情况【建议使用这个--不论项目中是否使用了RxJava2】
//运行时权限compile ‘com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar‘compile ‘io.reactivex.rxjava2:rxjava:2.0.2‘
如果想要实现条件触发获取权限(结合RxBinding使用)的情况,则还需要依赖RxBinding开源库
compile ‘com.jakewharton.rxbinding2:rxbinding:2.0.0‘
例如,Demo中的依赖配置如下:
三、在AndroidManifest.xml文件中声明权限列表
<!-- ======================授权获取设备ANDROID_ID========================== --><!-- 访问电话状态 --><uses-permission android:name="android.permission.READ_PHONE_STATE" /><!-- 允许程序读取外部存储文件 --><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><!-- 允许程序写入外部存储,如SD卡上写文件 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission><uses-permission android:name="android.permission.CAMERA" />
四、运行时权限申请代码
注意:需要import的相关类如下:
import com.jakewharton.rxbinding2.view.RxView;import com.tbruyelle.rxpermissions2.Permission;import com.tbruyelle.rxpermissions2.RxPermissions;import io.reactivex.functions.Action;import io.reactivex.functions.Consumer;
4.1 只有一个运行时权限申请的情况
/**只有一个运行时权限申请的情况*/private void onePermission(){ RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance rxPermissions.request(Manifest.permission.READ_PHONE_STATE) //权限名称,多个权限之间逗号分隔开 .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】 if (granted) { // 在android 6.0之前会默认返回true // 已经获取权限 Toast.makeText(MainActivity.this, "已经获取权限", Toast.LENGTH_SHORT).show(); String deviceId = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();//根据不同的手机设备返回IMEI,MEID或者ESN码 Toast.makeText(MainActivity.this, "{accept}deviceId=" + deviceId, Toast.LENGTH_SHORT).show(); } else { // 未获取权限 Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show(); } } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理 } }, new Action() { @Override public void run() throws Exception { Log.e(TAG,"{run}");//执行顺序——2 } });}
在Activity的onCreate方法中调用即可:
打印的日志如下:
4.2 同时请求多个权限(合并结果)的情况
/**同时请求多个权限(合并结果)的情况*/ private void MultPermission(){ RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance rxPermissions.request(Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE)//权限名称,多个权限之间逗号分隔开 .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) throws Exception { Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】 if (granted) { // 在android 6.0之前会默认返回true // 已经获取权限 Toast.makeText(MainActivity.this, "已经获取权限", Toast.LENGTH_SHORT).show(); } else { // 未获取权限 Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show(); } } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理 } }, new Action() { @Override public void run() throws Exception { Log.e(TAG,"{run}");//执行顺序——2 } }); }
在Activity的onCreate方法中调用即可:
打印的日志如下:
4.3 同时请求多个权限(分别获取结果)的情况
/**同时请求多个权限(分别获取结果)的情况*/ private void MultPermission2(){ RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance rxPermissions.requestEach(Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE)//权限名称,多个权限之间逗号分隔开 .subscribe(new Consumer<Permission>(){ @Override public void accept(Permission permission) throws Exception { Log.e(TAG, "{accept}permission.name=" + permission.name); Log.e(TAG, "{accept}permission.granted=" + permission.granted); if(permission.name.equals(Manifest.permission.READ_PHONE_STATE) && permission.granted){ // 已经获取权限 Toast.makeText(MainActivity.this, "已经获取权限", Toast.LENGTH_SHORT).show(); String deviceId = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();//根据不同的手机设备返回IMEI,MEID或者ESN码 Toast.makeText(MainActivity.this, "{accept}deviceId=" + deviceId, Toast.LENGTH_SHORT).show(); } } }); }
在Activity的onCreate方法中调用即可:
打印的日志如下:
4.4 条件触发获取权限(结合RxBinding使用)的情况
/**条件触发获取权限(结合RxBinding使用)的情况*/ private void clickPermission(View view){ RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance RxView.clicks(view) .compose(rxPermissions.ensure(Manifest.permission.CAMERA)) .subscribe(new Consumer<Boolean>() { @Override public void accept(Boolean granted) { Log.e(TAG, "{accept}granted=" + granted);//【多个权限的情况,只有所有的权限均允许的情况下granted==true】 if (granted) { // 在android 6.0之前会默认返回true // 已经获取权限 Toast.makeText(MainActivity.this, "已经获取CAMERA权限", Toast.LENGTH_SHORT).show(); } else { // 未获取权限 Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show(); } } }); }
在activity的onCreate方法中调用:
打印的日志如下:
其中,点击事件对应的控件如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.why.project.runtime.MainActivity"> <Button android:id="@+id/btn_getpermission" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="手动获取权限" android:layout_centerInParent="true"/></RelativeLayout>
混淆配置
无
参考资料
聊一聊 Android 6.0 的运行时权限
https://blog.coding.net/blog/understanding-marshmallow-runtime-permission
Android权限管理之Android 6.0运行时权限及解决办法
http://www.cnblogs.com/whoislcj/p/6064710.html
Android 6.0 运行时权限处理完全解析
http://blog.csdn.net/lmj623565791/article/details/50709663
Manifest.permission
https://developer.android.google.cn/reference/android/Manifest.permission.html
android权限大全
http://www.cnblogs.com/classic/archive/2011/06/20/2085055.html
RxPermissions开源库github地址:
https://github.com/tbruyelle/RxPermissions
项目demo下载地址
链接:http://pan.baidu.com/s/1mi9et1Q 密码:qru3
Android6.0运行时权限(基于RxPermission开源库)