Android动态权限申请

从Android 6.0开始,权限不再是在Manifest文件中声明一下即可申请,除了要在Manifest中填写以外,还要在程序中动态申请,即便用户授权,权限也不是永久的。

Android系统权限的概念:

Android是一个权限分隔的操作系统,每个应用都有独特的系统标识。一般情况下,应用没有权限执行对其它应用、系统、用户可能有不利影响的操作。每个应用都在应用沙盒中运行,因此当应用需要使用沙盒未提供的功能时,需要申请权限,比如读写sd卡、访问网络、访问其它应用的数据、读写联系人、调用摄像头等。权限在Manifest.xml文件中声明,Android 6.0以前,有的APP一股脑声明了各种各样的权限,用户可能没有细看就安装了,于是这些APP就可以为所欲为,无法无天。Android 6.0把权限分成正常权限和危险权限,AndroidManifest中声明的正常权限系统会自动授予,而危险权限则需要在使用的时候用户明确授予。就是Android 6.0以上的系统在第一次使用危险权限的时候,需要向用户申请,征得用户的同意。如果还是在没有权限的情况下执行操作就会Crash,错误日志为java.lang.SecurityException: Permission Denial。因此,应用对危险权限的申请,需要相应的处理。

危险权限和对应的权限组

危险权限都属于权限组,应用在向用户申请危险权限时,系统会弹对话框,描述应用要访问的权限组,这时候用户如果同意授权,则权限组包含的所有权限都会被系统授予。比如,应用被授予READ_EXTERNAL_STORAGE权限之后,如果再申请WRITE_EXTERNAL_STORAGE权限,系统会立即授予该权限。

危险权限表格如下

权限组 权限
CALENDAR READ_CALENDAR
WRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATION  ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE

 

具体的使用代码如下:

android.permission-group.CALENDAR
                          - android.permission.READ_CALENDAR
                          - android.permission.WRITE_CALENDAR

android.permission-group.CALL_LOG
                          - android.permission.READ_CALL_LOG
                          - android.permission.WRITE_CALL_LOG
                          - android.permission.PROCESS_OUTGOING_CALLS

android.permission-group.CAMERA
                          - android.permission.CAMERA

android.permission-group.CONTACTS
                          - android.permission.READ_CONTACTS
                          - android.permission.WRITE_CONTACTS
                          - android.permission.GET_ACCOUNTS

android.permission-group.LOCATION
                          - android.permission.ACCESS_FINE_LOCATION
                          - android.permission.ACCESS_COARSE_LOCATION

android.permission-group.MICROPHONE
                          - android.permission.RECORD_AUDIO

android.permission-group.PHONE
                          - android.permission.READ_PHONE_STATE
                          - android.permission.READ_PHONE_NUMBERS
                          - android.permission.CALL_PHONE
                          - android.permission.ANSWER_PHONE_CALLS
                          - com.android.voicemail.permission.ADD_VOICEMAIL
                          - android.permission.USE_SIP

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
                          - android.permission.RECEIVE_MMS

android.permission-group.STORAGE
                          - android.permission.READ_EXTERNAL_STORAGE
                          - android.permission.WRITE_EXTERNAL_STORAGE

 

申请权限

首先在manifest文件中声明

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

 使用时检查权限,没有权限则申请

 

判断是否拥有权限,使用ActivityCompat类中的checkSelfPermission方法,仅支持检测单个权限,如果需要检测多个权限,则使用逻辑运算符实现,这里以检测是否具有写存储空间权限和相机权限为例:
if (!(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
                        || !(ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)) {
        //没有权限,申请权限
}else {
       //拥有权限
}

 

在检测到没有权限的时候,使用ActivityCompat中的requestPermissions申请权限,这里同样是使用上面例子

String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
//申请权限,其中RC_PERMISSION是权限申请码,用来标志权限申请的
ActivityCompat.requestPermissions(MainActivity.this,permissions, RC_PERMISSION);

 

申请权限的结果我们可以在onRequestPermissionsResult方法中进行分析

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

      if (requestCode == RC_PERMISSION && grantResults.length == 2
              && grantResults[0] == PackageManager.PERMISSION_GRANTED
              && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
          Log.e(TAG, "权限申请成功");
      }else {
          Log.e(TAG, "权限申请失败");
      }
}

参数解释:

  • requestCode:权限申请码,标志是哪次权限申请
  • permissions:申请权限的字符串数组
  • grantResults:权限申请的结果,是一个整型数组,这个数组的长度就是申请权限的个数,并且数组元素就是对应每个权限的申请结果
使用兼容库
EasyPermissions是Google官方推荐的简化权限申请的第三方框架,下面简单介绍该框架的使用:
首先先在项目中添加依赖:
implementation 'pub.devrel:easypermissions:2.0.0'

 检查是否拥有权限

 
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                        Manifest.permission.CAMERA};
boolean hasPermissions = EasyPermissions.hasPermissions(EasyPermissionActivity.this, permissions);
if (hasPermissions) {
     //拥有权限
}else {
    //没有权限
}

申请权限

  • 首先重写onRequestPermissionsResult方法,将权限申请结果交给EasyPermissions处理
@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //Forward results to EasyPermissions
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

 

  • 让Activity或者是Fragment实现EasyPermissions.PermissionCallbacks接口,实现这个接口的onPermissionsGranted和onPermissionsDenied方法,这两个方法是EasyPermissions处理权限申请结果的回调
 @Override
    public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {

    }

    @Override
    public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            //用户勾选了“不再询问”,引导用户去设置页面打开权限
            new AppSettingsDialog.Builder(this)
                    .setTitle("权限申请")
                    .setRationale("应用程序运行缺少必要的权限,请前往设置页面打开")
                    .setPositiveButton("去设置")
                    .setNegativeButton("取消")
                    .setRequestCode(RC_SETTINGS_SCREEN)
                    .build()
                    .show();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == RC_SETTINGS_SCREEN) {
            //用户从设置页面返回,可以在这里检测用户是否打开了权限
        }
    }

 

  • 在需要申请权限的地方调用以下方法
@AfterPermissionGranted(RC_PERMISSIONS)
private void requestStoreAndCameraPermission() {
        String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
        if (EasyPermissions.hasPermissions(this, permissions)) {
            //权限获取成功
        }else {
            //没有权限,调用方法申请权限
            EasyPermissions.requestPermissions(this, "程序运行需要存储权限和相机权限", RC_PERMISSIONS, permissions);
        }
    }

 

添加AfterPermissionGranted注解的作用是,当权限申请成功之后,系统会自动调用一次该方法,这样会直接走权限获取成功的业务逻辑;
EasyPermissions.requestPermissions方法的第二个参数的意义是当用户第一次拒绝了权限之后,第二次调用该方法获取权限时,会先弹出一个对话框向用户解释为何需要该权限,对话框的内容就是这个参数(弹出的对话框样式在这里无法自己定义

RxPermissions也是一个替代方案,而且该框架提供了处理用户拒绝授权并且勾选了“不在询问”后的处理方法

posted @ 2020-07-21 17:40  火热火热7  阅读(1658)  评论(0编辑  收藏  举报