美文网首页
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