Android 权限管理申请介绍



一、引言

动态权限管理是 Android 6.0(Build.VERSION_CODES.M = Api23)推出的,提醒用户当前 APP 所需要的权限,防止滥用。这些权限一般分为两种:

  • 普通权限:直接manifest清单文件中写上注册就行了
  • 危险权限:需要动态申请

这里我们按照原生、GitHub 上比较出名的第三方权限管理工具 RxPermissions 及 EasyPermissions 为例说明权限的应用。

二、权限说明

在 Android 6.0 如果不对关键权限作动态申请,则运行时就会出现异常:

FATAL EXCEPTION: main
AndroidRuntime: Process: com.cchip.smartwelding.weld, PID: 3496
AndroidRuntime: java.lang.RuntimeException: Unable to resume activity {com.cchip.smartwelding.weld/com.cchip.smartwelding.btcontrol.activities.MainActivity}: java.lang.SecurityException: com.cchip.smartwelding.weld was not granted  this permission: android.permission.WRITE_SETTINGS.

流程:

  • 第一次询问权限 -》允许权限(再次询问同样权限也不会弹框,已注册)
  • 第一次询问权限-》拒绝权限-》再次询问权限-》允许权限(再次询问同样权限也不会弹框,已注册)
  • 第一次询问权限-》拒绝权限-》再次询问权限-》不再询问(再次询问同样权限也不会有弹框,直接拒绝)

三、原生权限申请

原生权限比较长且繁琐,但相对也好理解。

   private void initPermission() {
        ////判断当前系统是否高于或等于6.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS};

            List<String> denyPermissions = new ArrayList<>();
            for (String permission : permissions) {
                //已注册权限
                if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, permission)) {
                    denyPermissions.add(permission);
                    //进入到这里代表没有权限.
                }
            }

            if (!denyPermissions.isEmpty()) {
                //未注册权限,申请权限
                requestPermissions(denyPermissions.toArray(new String[denyPermissions.size()]), 101);
            }
        }
    }

四、RxPermissions 权限管理

GitHub: RxPermissions

RxPermissions 是帮助开发者简化 requestPermissions() 相关的处理。使用起来觉得很简洁,最方便在 activity 的 onCreate() 中使用。

4.1 依赖配置

在 APP 的 build.gradle 中依赖 rxjava,rxandroid 和 rxpermissions

    implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.4@aar'   //最新为 V0.10.2

4.2 初始化

在 onCreate 中初化

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ButterKnife.bind(this);
        getPermissions();
        ......
    }
    private void getPermissions() {
        RxPermissions rxPermissions = new RxPermissions(this);

        rxPermissions.requestEach(Manifest.permission.RECORD_AUDIO,
                                  Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                  Manifest.permission.READ_CONTACTS,
                                  Manifest.permission.CALL_PHONE,
                                  Manifest.permission.READ_PHONE_STATE,
                                  Manifest.permission.ACCESS_COARSE_LOCATION,
                                  Manifest.permission.ACCESS_FINE_LOCATION,
                                  Manifest.permission.READ_EXTERNAL_STORAGE)
            .subscribe(new Consumer<Permission>() {
                boolean isDeniy = false;

                @Override
                public void accept(Permission permission) throws Exception {
                    if (permission.granted) {
                        // 用户已经同意该权限
                        if (permission.name.equals(Manifest.permission.READ_CONTACTS)) {
                            ContactUtils.getInstance().SearchAllContact();
                        } else if (permission.name.equals(Manifest.permission.ACCESS_COARSE_LOCATION)
                                   || permission.name.equals(Manifest.permission.ACCESS_FINE_LOCATION)) {
                            logShow("ACCESS_FINE_LOCATION");
                            GaodeUtils.getInstance().startLocation();
                        }
                    } else if (permission.shouldShowRequestPermissionRationale) {
                        // 用户拒绝了该权限,没有选中『不再询问』(Never ask again),那么下次再次启动时,还会提示请求权限的对话框
                        isDeniy = true;
                    } else {
                        isDeniy = true;
                    }
                    if (isDeniy && MainActivity.this.firstToast) {
                        // 用户拒绝了该权限,并且选中『不再询问』
                        MainActivity.this.firstToast = false;
                        Toast.makeText(MainActivity.this, getString(R.string.permissions_deniy), Toast.LENGTH_LONG).show();
                    }
                }
            });
    }

五、EasyPermissions 权限管理

GitHub: EasyPermissions

EasyPermissions 框架是 Google 提供的,是一个简化基本的系统权限逻辑的库。

5.1 依赖配置

在 APP 的 build.gradle 中依赖 easypermissions

implementation 'pub.devrel:easypermissions:1.1.2'   //最新为V2.0.0

5.2 重写 onRequestPermissionsResult

开始使用 EasyPermissions, 必须让你的 Activity 或者 Fragment 重写 onRequestPermissionsResult 方法:

public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

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

        // Forward results to EasyPermissions
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

    @Override
    public void onPermissionsGranted(int requestCode, List<String> list) {
        // Some permissions have been granted
        // ...
    }

    @Override
    public void onPermissionsDenied(int requestCode, List<String> list) {
        // Some permissions have been denied
        /**
         * 若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。
         * 这时候,需要跳转到设置界面去,让用户手动开启。
         */
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            new AppSettingsDialog.Builder(this).build().show();
        }
    }
}

5.3 请求权限 getPermission()方法

     public void getPermission() {
        //perms内填写需要动态申请的权限Manifest.permission.ACCESS_COARSE_LOCATION为作者随意写的(根据需要)
        String[] perms = {Manifest.permission.ACCESS_COARSE_LOCATION};
        //动态权限
        if (EasyPermissions.hasPermissions(this, perms)) {
            //有对应权限的操作
        } else {  
            //没有对应权限的操作,"XXX权限"即为申请的权限名称
            EasyPermissions.requestPermissions(getActivity, "XXX权限",QX_XX, perms);
        }
    }

六、总结

随着 Google 加强安全管理,及各大平台要求 TargetSdkVersion >= 26,在 APP 开发中,动态权限申请是不得不考虑的问题,否则一运行到就 Crash。