首页 > 代码库 > 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开源库)