美文网首页
android 6.0权限动态申请

android 6.0权限动态申请

作者: 程序员小华 | 来源:发表于2019-01-06 17:49 被阅读0次

    Android 6.0系统一个最大的特性就是动态权限申请。在android 6.0以前,我们APP开发中对权限的处理是直接在AndroidManifest文件中配置即可;这种情况在android 6.0之后就改变了,对于一些涉及用户隐私的危险权限,我们不但需要在AndroidManifest文件中进行配置,还需要在使用到该权限的地方使用API来对权限进行动态申请,接下来本文通过Android原生API、EasyPermissions框架、RxPermissions框架等三种方式介绍怎么在开发过程中动态申请危险权限。

    一、危险权限和权限组

    android官方文档对需要动态申请的权限进行了列举如下:

    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
    

    如果要使用以上危险权限,除了需要在AndroidManifest文件中配置,还需要在代码中对这些权限进行动态申请

    二、原生API实现动态权限申请
    1.判断是否拥有权限,使用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 {
           //拥有权限
    }
    
    2.在检测到没有权限的时候,使用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:权限申请的结果,是一个整型数组,这个数组的长度就是申请权限的个数,并且数组元素就是对应每个权限的申请结果
    3.上面几步是常用的权限申请的流程,但是并没有对用户点击“不再提醒”进行处理

    在权限申请过程中,如果第一次权限申请被用户拒绝了,那么第二次申请该权限的时候,在权限申请框上会出现一个“不再询问”的勾选,如果用户勾选了这个选项并拒绝的时候,那以后每次申请权限都直接返回权限拒绝


    点击了不再提示之后,权限申请时不会再弹出权限申请提示框了,而是直接返回失败,在这种情况下,我们应该引导用户去设置里把权限打开,但是android API中并没有判断用户是否勾选了“不再询问”的监听,那我们开发者怎么去判断用户是否勾选了“不再询问”呢?
    这个可以通过shouldShowRequestPermissionRationale(),这个访问的作用是是否需要向用户解释为何需要申请该权限,当首次申请权限时,该方法返回false,第二次申请权限会返回true。当第二次申请权限并且用户勾选了“不再询问”时该方法返回的是false,因此我们可以推导出,如果用户第二次申请权限被拒绝并且shouldShowRequestPermissionRationale()返回false,那么用户一定是勾选了不再询问,接下来可以通过以下代码引导用户前往设置页面打开权限:
    new AppSettingsDialog.Builder(this)
                        .setTitle("权限申请")
                        .setRationale("应用程序运行缺少必要的权限,请前往设置页面打开")
                        .setPositiveButton("去设置")
                        .setNegativeButton("取消")
                        .setRequestCode(RC_SETTINGS_SCREEN)
                        .build()
                        .show();
    /**
    *从设置页面返回,可以再次检查权限是否已打开
    */
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
    
        if (requestCode == AppConstants.RC_SETTINGS_SCREEN) {
            // Do something after user returned from app settings screen, like showing a Toast.
            LG.e("权限申请结果: 从设置页面返回");
        }
    }
    
    三、权限申请框架EasyPermissions简单使用

    EasyPermissions是Google官方推荐的简化权限申请的第三方框架,下面简单介绍该框架的使用:
    首先先在项目中添加依赖:

    implementation 'pub.devrel:easypermissions:2.0.0'
    

    1.检查是否拥有权限
    String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                            Manifest.permission.CAMERA};
    boolean hasPermissions = EasyPermissions.hasPermissions(EasyPermissionActivity.this, permissions);
    if (hasPermissions) {
         //拥有权限
    }else {
        //没有权限
    }
    
    2.申请权限
    • 首先重写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简单使用

    RxPermissions也是一款比较好用的权限申请框架,可以搭配着RxJava等一起使用,RxPermissions没有检测是否拥有权限的api,使用的时候是直接调用申请权限的方法。

    • 首先引入RxPermissions依赖包
    //项目Project的build.gradle文件
    allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }
    
    //模块app的build.gralde文件
    dependencies {
        implementation 'com.github.tbruyelle:rxpermissions:0.10.2'
    }
    
    • 申请单个权限,这个方法没有处理用户勾选“不再询问”的处理
            RxPermissions rxPermissions = new RxPermissions(this);
            rxPermissions.request(Manifest.permission.CAMERA)
                    .subscribe(granted -> {
                        if (granted) {
                            //拥有权限,在系统版本小于M时granted恒为true
                        } else {
                            //权限拒绝
                        }
                    });
    
    • 申请多个权限,并且每个权限申请的结果分别返回
            RxPermissions rxPermissions = new RxPermissions(this);
            //在这里申请两个权限,两个申请结果分别返回,即回调两次
            rxPermissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.CAMERA)
                    .subscribe(permission -> {
                        if (permission.granted) {
                            //获得权限成功
                        } else if (permission.shouldShowRequestPermissionRationale) {
                            //获取权限失败,但是用户没有勾选”不再询问“,在这里应该弹出对话框向用户解释为何需要该权限
                        }else {
                            //权限申请失败,用户勾选了“不再询问”,在这里应该引导用户去设置页面打开权限
                        }
                    });
    
    • 申请多个权限,并且所有权限结果统一为一个返回,即要不返回成功,要不失败
            RxPermissions rxPermissions = new RxPermissions(this);
            rxPermissions.requestEachCombined(Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.CAMERA)
                    .subscribe(permission -> {   //只回调一次
                        if (permission.granted) {
                            //全部权限获取成功
                        } else if (permission.shouldShowRequestPermissionRationale) {
                            //至少一个权限获取失败,但是用户没有勾选”不再询问“,在这里应该弹出对话框向用户解释为何需要该权限
                        }else {
                            //至少一个权限申请失败,用户勾选了“不再询问”,在这里应该引导用户去设置页面打开权限
                        }
                    });
    
    四、总结

    本文记录了三种方式进行android 6.0动态权限申请,但是目前我比较常用的是RxPermissions 这个库,使用起来比较简便,而且也可以对用户勾选“不再询问”的操作进行处理。

    相关文章

      网友评论

          本文标题:android 6.0权限动态申请

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