美文网首页
RuntimePermission 运行时权限解析

RuntimePermission 运行时权限解析

作者: 程起蒙 | 来源:发表于2018-07-20 04:07 被阅读285次

    概述

    Android 6.0开始,为了更好的保护用户隐私,引入了新的权限机制:

    • 普通权限与Android 6.0之前一样,直接在Manifest中声明即可
    • 危险权限在使用之前告知用户.获得用户授权之后才可以获取相应的权限
    • 用户可以选择不再提醒永久拒绝某个权限
    危险权限列表

    粗体字为权限组,权限组下面为权限组内的具体权限</br>
    申请权限组中的任何一个权限即获得了整个权限组中的所有权限

    CONTACTS
    • READ_CONTACTS
    • WIRTE_CONTACTS
    • GET_CONTACTS
    PHONE
    • READ_CALL_LOG
    • READ_PHONE_STATE
    • CALL_PHONE
    • WRITE_CALL_LOG
    • USE_SIP
    • PROCESS_OUTGOING_CALLS
    • ADD_VOICEMAIL
    CALENDAR
    • READ_CALENDAR
    • WRITE_CALENDAR
    CAMERA

    CAMERA

    SENSORS

    BODY_SENSORS

    LOCATION
    • ACCESS_FINE_LOCATION
    • ACCESS_COARSE_LOCATION
    STORAGE
    • READ_EXTERNAL_STORAGE
    • WRITE_EXTERNAL_STORAGE
    MICROPHONE
    • RECORD_AUDIO
    SMS
    • READ_SMS
    • RECEIVE_WAP_PUSH
    • RECEIVE_MMS
    • RECEIVE_SMS
    • SEND_SMS
    • READ_CELL_BROADCASTS

    使用安卓自带Api申请运行时权限

    以获取用户通讯录为例
    1. 在Manifest中声明我们需要的权限
    //in Manifest
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    
    1. 在Accitity中申请权限
      大致的逻辑:</br>
      -> 检查用户是否授权过该权限
      -> 已经授权过 -> 执行业务逻辑
      -> 没有授权 -> 申请权限 -> 成功 -> 执行业务逻辑
    API:
    • 检查用户是否授权过该权限
      int checkSelfPermission()
      返回值:</br>
      PackageManager.PERMISSION_GRANTED 表示有权限
      PackageManager.PERMISSION_DENIED 表示无权限

    • 获取权限
      void requestPermissions()
      三个参数:
      Context
      new String[]{} 需要申请权限的字符串数组
      RequestCode 自定义的请求码,结果回调的方法中使用,判断是哪个权限的申请

    • 权限请求的结果回调
      void onRequestPermissionsResult()

    Example:
    // 申请一个读取通讯录的权限
    private final static int PERMISSION_CONTACT = 1;
    
    private void getPermission() {
        if (ContextCompat.checkSelfPermission(thisActivity,Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED) {
            // 无权限时去请求权限
            ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_CONTACTS},PERMISSION_CONTACT);
        
        }else{
            // 有权限直接进行业务操作
            doSomeWork();
        }
    }
    
    /**
    * 直接在Activity重写 onRequestPermissionsResult()方法即可
    * requestCode requetsPermissions()方法中传入的RequestCode
    * String[] permissons  requetsPermissions()方法中传入的permission数组
    * grantResults  返回的具体结果
    **/
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults) {
        switch(requestCode) {
            case REQUEST_CONTACT:
                if (grantResults.length > 0  && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 授予权限,拨打电话
                doSomeWork();
                } else {
                    Toast.makeText(this, "请求权限被拒绝", Toast.LENGTH_SHORT).show();
                }
             break;
        }
    }
    
    
    用户拒绝权限的处理

    拒绝的逻辑:
    申请权限 -> 用户拒绝 -> END
    再次申请权限 -> 权限弹框会多一个不再提醒的选项 -> 用户勾选不再提醒选项并拒绝权限 -> END Forever

    为了我们业务的正常进行,我们需要避免 END FOREVER的结果,所以在第二次申请权限的时候我们要提醒用户不要拒绝我们的权限,安卓也为我们提供了这样的API

    • boolean shouldShowRequestPermissionRationale() 是否给用户提醒权限的必要性
    在代码中区分是第一次还是被拒绝后再次申请权限
    // 重写一下getPermission()方法
    private void getPermission() {
        if (ContextCompat.checkSelfPermission(thisActivity,Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED) {
            // 无权限时去请求权限
            if(ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
                Manifest.permission.READ_CONTACTS)) {
                // 通常是弹框告诉用户权限的必要性,然后继续申请权限
                
                new AlertDialog.Builder(this)
                    .setCancelable(false)
                    .setMessage("求求你别拒绝~")
                    .setPositiveButton("好的", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_CONTACTS},PERMISSION_CONTACT);
                    }
                })
                .setNegativeButton("不给", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                })
                .show();
            } else {
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_CONTACTS},PERMISSION_CONTACT);
            }
        }else{
            // 有权限直接进行业务操作
            doSomeWork();
        }
    }
    

    使用第三方框架申请权限

    由于国内的各家手机厂商都对安卓进行了魔改,安卓所提供的原生的API可能会在一些手机上报错,或者无法正常申请权限。对种类繁多的手机Rom和安卓版本都进行适配会非常繁琐,推荐大家使用一些成熟的第三方框架去获取手机权限。

    框架并不能解决所有的问题

    • 写博客之前碰到的问题:
      我的App需要申请相机权限,本人用的是小米/华为的手机,当时选择了使用AndPermission申请权限。测试了几次之后都挺正常。然后使用同事的魅族手机测试,在用户第一次拒绝权限之后,第二次请求权限 gradResults 返回的是Granted.所以代码会在没有权限的情况下继续向后执行,相机界面变成了一片黑色.
      然后我又用原生Api尝试了一下,结果是一样的.所以原因应该是魅族手机的权限逻辑问题.
      接下来去找解决问题的方案. 在AndPermission的Issus里面作者是这样解释的

    在有些国产机上,申请权限时系统返回的总是有权限,对于通讯录、读写SD卡等权限,AndPermission的策略是拿到有权限的结果后,再执行一下读取一条通讯录或者向SD卡写入一个文件来验证是否真的有权限。但是对于拨打电话此类权限,AndPermission不能真的去拨打一个电话,因此直接回调了onGranted(),开发者可以在onGranted()中直接拨打电话,来配合AndPermission申请权限,如果有就自然拨打出去了,如果没有权限,上述手机会弹出授权对话框或者抛出异常,如果抛出异常,AndPermission会重新回调到onDenied()方法,开发者即可认为是没有权限。

    简单分析下作者所说的内容:
    通常的逻辑: getPermission -> granted() -> success
    严谨的逻辑: getPermission -> granted() -> do() -> 捕获由于权限造成的异常 -> onDenied() -> getPermission()

    针对我上面的相机权限问题: 在申请权限成功进入相机界面之后,在相机启动得时候相机打开失败/获取不到相机参数时,重新弹框去告诉用户.所以还是得具体情况具体分析.

    最后总结几个申请权限的注意事项:

    1. 在需要的时候申请权限,不要在App开屏的时候请求所有的权限。体验非常不友好,而且容易被拒绝
    2. 对于一些必要权限(用户拒绝概率较高),在申请权限之前弹框提示告知用户.
    3. 有些手机可能会返回错误的权限信息,获取到权限提供的数据才算结束
    4. 参考AndPermission的做法,我们可以在代码中抛出由于没有权限引起的异常,再次申请权限。或者显示ErrorView,让用户点击授权。

    相关文章

      网友评论

          本文标题:RuntimePermission 运行时权限解析

          本文链接:https://www.haomeiwen.com/subject/oyzxmftx.html