Android6.0运行时权限解析,RxPermissions的使用,自己封装一套权限框架

Android6.0运行时权限解析,RxPermissions的使用,自己封装一套权限框架



在Android6.0中,新增加了一个运行时的权限,我相信很多人都已经知道了,估计也知道怎么用了,这篇博客很简单,就是告诉大家如何去申请运行时权限和RxPermission这个权限框架的使用,同时根据现有的技术封装思想,去封装一个自己可用的权限框架,好的,我们继续往下看

一.Android M 运行时权限介绍

关于Android M的更新变化,我就不啰嗦了,有兴趣的可以看下Android M更新

而我们的这篇文章,也是直接参考的Google api文档中关于运行时权限这一块的来相应的讲解Google API 运行时权限

先来说一些概念性的东西,从 Android 6.0(API 级别 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。此方法可以简化应用安装过程,因为用户在安装或更新应用时不需要授予权限。它还让用户可以对应用的功能进行更多控制;这句话的意思就是你安装的时候,Android6.0之前,假设你看到很多权限,有一两个权限你不想给他,但是如果你不给他,就无法安装,可是像QQ,微信这样的应用,是你不想安装就不想安装的吗?这是非常流氓的,而运行时权限出来后,正常安装,但是如果你想使用这个功能,再去申请权限,这就比较合理了,我们接着往下看

系统权限分为两类:正常权限和危险权限:

  • 正常权限不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。
  • 危险权限会授予应用访问用户机密数据的权限。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。

这里也比较好理解,危险权限我们需要去申请就OK了,如果你搞不清楚这些权限的区别,这里我推荐你去看正常权限和危险权限 ,在这个表中,列举了所有的权限,你可以根据自己的需求去搜索

我们说了这些概念,其实我觉得你们都会了,那我们直接进入代码环节吧

二.申请权限

权限你可以申请单个,也可以申请多个,我们一步步看,假设我们以打电话的权限为例子,我们在清单文件中填写电话权限

<uses-permission android:name="android.permission.CALL_PHONE"/>

正常来讲,我们只需要调用这段打电话的代码就可以拨打电话了,所有我们写了一个打电话的方法,但是他却报错了,那是因为我们使用的targetSdkVersion是25,大于23,所有他会检查权限,也就是这样

他警告我们需要去判断权限,那我们就按照他的提示一步步来,首先判断是否同意了该权限

    //正常获取权限
    private void checkPermissionForNormal() {
        //判断是否同意此权限
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
            //如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.CALL_PHONE)) {
                Toast.makeText(this, "你之前拒绝过此权限", Toast.LENGTH_SHORT).show();
            } else {
                //申请权限
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 100);
            }
        } else {
            callPhone();
        }
    }

这代码看起来还是比较正常的,首先他会去检查你当前的权限是否等于PackageManager.PERMISSION_GRANTED,0为成功,-1为失败,如果他不等于0,那我就去检查你之前是否请求过该权限,同时你点击了拒绝。

注:如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 不再提醒 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false。

好的,如果都没有,那我就通过ActivityCompat的requestPermissions方法去请求权限,里面的几个参数要注意一下,第一个是上下文,第二个是权限数组,也就是说他支持单个和多个权限的申请,第三个是回调的resultCode,我们来运行一下,看下他是如何申请的

好的,那我们现在来处理一下结果吧,实现一下onRequestPermissionsResult方法

   //权限的回调
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 100: {
                //返回的结果数组大于0说明有结果
                if (grantResults.length > 0
                        //因为我们只判断了一个打电话的权限,所有是数组的0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "同意了权限", Toast.LENGTH_SHORT).show();
                    callPhone();
                } else {
                    Toast.makeText(this, "拒绝了权限", Toast.LENGTH_SHORT).show();
                }
                return;
            }
        }
    }

这段代码的注释也很清楚,我们只要判断有结果,然后就可以去做相应的,处理了,这个是单个的权限

我们再来看下多个权限的申请,首先是申请了

   //正常获取权限
    private void checkPermissionForNormal() {
        //判断是否同意此权限
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this,
                        Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(this,
                        Manifest.permission.SYSTEM_ALERT_WINDOW) != PackageManager.PERMISSION_GRANTED) {
            //申请权限
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE, Manifest.permission.CAMERA
                    , Manifest.permission.SYSTEM_ALERT_WINDOW}, 100);
        }
    }

我在这里申请了一个电话,一个相机的权限,还有一个窗口权限,那我们结果的处理如何呢

    //权限的回调
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case 100: {
                if (grantResults.length > 0) {
                    for (int i = 0; i < grantResults.length; i++) {
                        if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                            Toast.makeText(this, "同意权限", Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(this, "拒绝权限", Toast.LENGTH_SHORT).show();
                        }
                    }
                } else {
                    Toast.makeText(this, "拒绝了权限", Toast.LENGTH_SHORT).show();
                }
                return;
            }
        }
    }

同样的我们只需要去判断我们的返回值0或者-1就可以了,那我们来看下最终的演示结果

这里要注意一点的就是你计算申请了运行时权限,你的清单文件中,也还是要加入添加相应的权限

    <uses-permission android:name="android.permission.CALL_PHONE"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

三.RxPermissions

RxPermissions是一个封装的权限库,因为使用比较简单,所有我也提出来给大伙讲讲,地址可以参考RxPermissions GitHub

先来说一下这个RxPermissions库的集成工作,因为他是跟着RxJava一起的,如果要使用,还得添加RxJava,而且RxJava有两个版本,我们这里以RxJava2为例子

添加依赖

    //RxPermissions
    compile 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.3@aar'
    //RxJava2
    compile "io.reactivex.rxjava2:rxjava:2.0.0"

他的使用如果用正常的RxJava语法,那就是这样:

RxPermissions rxPermissions = new RxPermissions(MainActivity.this);
rxPermissions.request(Manifest.permission.CALL_PHONE)
        .subscribe(new Observer<Boolean>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onNext(Boolean value) {
                if(value){
                    Toast.makeText(MainActivity.this, "同意权限", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(MainActivity.this, "拒绝权限", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });

这样我们可以申请到权限了,如图

当然如果你使用lambda表达式,你会更爽的

RxPermissions rxPermissions = new RxPermissions(MainActivity.this);
rxPermissions
        .request(Manifest.permission.CALL_PHONE)
        .subscribe(granted -> {
            if (granted) {
                Toast.makeText(MainActivity.this, "同意权限", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(MainActivity.this, "拒绝权限", Toast.LENGTH_SHORT).show();
            }
        });

四.权限封装

关于权限的封装其实还是比较纠结的,我们分析下,首先判断权限,是没有什么问题的,申请权限也是没有什么问题的,但是处理结果就麻烦了,他是在Activity的回调中,其实在fragment也有这个回调处理,所有现在市面上比较多的就是fragment中处理,而我们上面的RxPermissions也是这样处理的,我们看一下他的源码

 private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {
        RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);
        boolean isNewInstance = rxPermissionsFragment == null;
        if (isNewInstance) {
            rxPermissionsFragment = new RxPermissionsFragment();
            FragmentManager fragmentManager = activity.getFragmentManager();
            fragmentManager
                    .beginTransaction()
                    .add(rxPermissionsFragment, TAG)
                    .commitAllowingStateLoss();
            fragmentManager.executePendingTransactions();
        }
        return rxPermissionsFragment;
    }

他把这个fragment add在这个activity中,然后再fragment中处理,说实话,挺巧妙的,那我们今天就换一种方式来处理,这就是我们的统配Activity中处理,而我现在教大家另一种封装的方法,那就是实现一个Activity的基类,先看下我们如何去使用的

checkPermissions(new String[]{Manifest.permission.CALL_PHONE,
        Manifest.permission.CAMERA,}, 300, new PermissionsResultListener() {
    @Override
    public void onSuccessful(int[] grantResults) {
        for (int i = 0; i < grantResults.length; i++) {
            if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(MainActivity.this, "同意权限", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(MainActivity.this, "拒绝权限", Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    public void onFailure() {
        Toast.makeText(MainActivity.this, "失败", Toast.LENGTH_SHORT).show();
    }
});

可以发现,这里我们就一行代码,checkPermissions调用,里面的参数一个是权限数组,一个是返回码,还有一个是回调,那如何去做呢,其实就是实现一个接口

public interface PermissionsResultListener {

    //成功
    void onSuccessful(int[] grantResults);

    //失败
    void onFailure();
}

以及在PermissionsActivity中完成它的操作,不过有一个弊端就是需要继承PermissionsActivity,所有我们要写我们的checkPermissions方法,就需要继承这个PermissionsActivity,而这里面的代码说不上难

public class PermissionsActivity extends AppCompatActivity {

    private PermissionsResultListener mListener;
    private int mRequestCode;
    private List<String> mListPermissions = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    protected void checkPermissions(String[] permissions, int requestCode, PermissionsResultListener listener) {
        //权限不能为空
        if (permissions != null || permissions.length != 0) {
            mListener = listener;
            mRequestCode = requestCode;
            for (int i = 0; i < permissions.length; i++) {
                if (!isHavePermissions(permissions[i])) {
                    mListPermissions.add(permissions[i]);
                }
            }
            //遍历完后申请
            applyPermissions();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == mRequestCode) {
            if (grantResults.length > 0) {
                mListener.onSuccessful(grantResults);
            } else {
                mListener.onFailure();
            }
        }
    }

    //判断权限是否申请
    private boolean isHavePermissions(String permissions) {
        if (ContextCompat.checkSelfPermission(this, permissions) != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
        return true;
    }

    //申请权限
    private void applyPermissions() {
        if (!mListPermissions.isEmpty()) {
            int size = mListPermissions.size();
            ActivityCompat.requestPermissions(this, mListPermissions.toArray(new String[size]), mRequestCode);
        }
    }
}

这就是我们的权限封装了,到这里你应该明白或者说掌握了运行时权限的绝大部分操作吧,我们运行一下看下

Sample下载

有兴趣的加群讨论:555974449


付费群

时间: 2024-09-25 12:56:19

Android6.0运行时权限解析,RxPermissions的使用,自己封装一套权限框架的相关文章

谈谈Android6.0运行时的权限处理

运行时权限介绍 Android 6.0在我们原有的AndroidManifest.xml声明权限的基础上, 又新增了运行时权限动态检测,以下权限都需要在运行时判断: 1.身体传感器 2.日历 3.摄像头 4.通讯录 5.地理位置 6.麦克风 7.电话 8.短信 9.存储空间 在 Android 6.0 中,app 如果想要获得某些权限,会在应用中弹出一个对话框,让用户确认是否授予该权限. 具体的截图如下: 这要做的好处就是运行一个 app 时可以拒绝其中的某些权限,防止 app 触及到你的隐私(

详解Android6.0运行时权限管理_Android

自从Android6.0发布以来,在权限上做出了很大的变动,不再是之前的只要在manifest设置就可以任意获取权限,而是更加的注重用户的隐私和体验,不会再强迫用户因拒绝不该拥有的权限而导致的无法安装的事情,也不会再不征求用户授权的情况下,就可以任意的访问用户隐私,而且即使在授权之后也可以及时的更改权限.这就是6.0版本做出的更拥护和注重用户的一大体现. 一.认知 今天我们就来学习下Android6.0的权限管理. Android6.0系统把权限分为两个级别: 一个是Normal Permiss

详解Android6.0运行时权限管理

自从Android6.0发布以来,在权限上做出了很大的变动,不再是之前的只要在manifest设置就可以任意获取权限,而是更加的注重用户的隐私和体验,不会再强迫用户因拒绝不该拥有的权限而导致的无法安装的事情,也不会再不征求用户授权的情况下,就可以任意的访问用户隐私,而且即使在授权之后也可以及时的更改权限.这就是6.0版本做出的更拥护和注重用户的一大体现. 一.认知 今天我们就来学习下Android6.0的权限管理. Android6.0系统把权限分为两个级别: 一个是Normal Permiss

详解Android权限管理之Android 6.0运行时权限及解决办法_Android

前言: 今天还是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在Android 6.0以上设备越来越多了,所以Android 6.0 权限适配是必不可少的工作,这里主要介绍一下我们公司是如何做Android 6.0权限适配的. Android 6.0以下非运行时权限: 根据上面博客我们很清楚的知道,Android的权限其实就是为了程序之间更加的安全的访问,所以权限有等级之分,比如:No

详解Android权限管理之Android 6.0运行时权限及解决办法

前言: 今天还是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在Android 6.0以上设备越来越多了,所以Android 6.0 权限适配是必不可少的工作,这里主要介绍一下我们公司是如何做Android 6.0权限适配的. Android 6.0以下非运行时权限: 根据上面博客我们很清楚的知道,Android的权限其实就是为了程序之间更加的安全的访问,所以权限有等级之分,比如:No

iOS学习之Objective-C 2.0 运行时系统编程

0 导言 本主主要内容包括: 1.概述 2.参考 3.运行时系统的版本和平台 4.和运行时系统的交互 5.消息 6.动态方法解析 7.消息转发 8.类型编码 9.属性声明 1 概述 Objective-C语言将决定尽可能的从编译和链接时推迟到运行时.只要有可能,Objective-C总是使用动态的方式来解决问题.这意味着Objective-C语言不仅需要一个编译器,同时也需要一个运行时系统来执行编译好的代码.这里的运行时系统扮演的角色类似于 Objective-C语言的操作系统,Objectiv

ASP.NET 2.0运行时简要分析

概述: 本文基于ASP.NET 2.0的源代码,对ASP.NET 2.0运行时进行了简要的分析, 希望能帮助你理解ASP.NET 2.0中请求处理过程及页面编译模型. 关键字: ASP.NET 2.0运行时,原理,请求处理,页面编译,ASP.NET 2.0 HTTP Runtime 主要类: System.Web.HttpRuntime System.Web.HttpApplicationFactory System.Web.HttpApplication System.Web.Compilat

详解Android数据存储之Android 6.0运行时权限下文件存储的思考_Android

前言: 在我们做App开发的过程中基本上都会用到文件存储,所以文件存储对于我们来说是相当熟悉了,不过自从Android 6.0发布之后,基于运行时权限机制访问外置sdcard是需要动态申请权限,所以以往直接sdcard根目录上直接新建了一个xxx/cache/目录来做文件存储就会不是那么容易控制了,所以有必要重新认识一下Android文件存储的相关知识了. 背景: 有关外置sdcard的读写权限 <uses-permission android:name="android.permissi

Android 6.0 运行时权限处理问题

序 自从升级到Android M以来,最大的改变就是增加了运行时权限RuntimePermission,6.0以上的系统如果没有做适配,运行了targetSDK=23的App时就会报权限错误.我们知道6.0以下的系统是按照的时候权限申请的,6.0和之后的版本是我们想要使用某个app的权限,去动态申请的,这也是基于安全上的考虑吧(比如:单机的象棋对战,请求访问通讯录权限等不合理的权限,这肯定是有问题的). 为了保护用户的隐私,谷歌官方将权限分为了两类,一个是正常权限(Normal Permissi