Android权限申请

一、基本介绍

1、正常权限(安装时权限):

不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予该权限。

例如,设置时区的权限就是正常权限。如果应用声明其需要正常权限,系统会自动向应用授予该权限。

2、危险权限(运行时权限):

会授予应用访问用户机密数据的权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。

Android权限申请
Android权限申请

3、注意:

  1. 在 Android 5.1(API 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。(沿用之前的权限系统),也就是在程序安装的时候程序会将所有需要的权限全部列出来,其设计思路非常简单,就是用户如果认可你所申请的权限,那么就会安装你的程序,如果不认可你所申请的权限,则会拒绝安装。
  2. 从Android6.0版本开始才开始加入运行时权限,也就是说用户不需要在安装软件的时候一次性授权所有的权限,而是可以在软件的使用过程中再对某一项权限进行申请使用,这就是运行时权限。
  3. 在请求权限时,系统只告诉用户应用需要的权限组,而不告知具体权限。(如上图)

二、运行时权限(使用原生API)

1、步骤

  1. 首先要在清单文件中申请所需要的权限。
    Android权限申请

  2. 将硬件声明为可选(uses-feature)以及要确定硬件可用性
    Android权限申请

Android权限申请
3. 按API级别申明权限

当目标sdk大于等于29的时候配置android.permission.WRITE_EXTERNAL_STORAGE会出现下面的警报:

WRITE_EXTERNAL_STORAGE no longer provides write access when targeting Android 10+

这时候需要设置根据Api级别申明权限android:maxSdkVersion,该属性表示设备搭载的系统版本高于 maxSdkVersion 时不需要特定权限。

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28"
        tools:ignore="ScopedStorage"/>
  1. 请求应用权限,其逻辑应该如下图所示
    Android权限申请

  2. 首先检查是否已经授予了某个权限,使用 ContextCompat.checkSelfPermission()此方法会返回 PERMISSION_GRANTEDPERMISSION_DENIED。如果 ContextCompat.checkSelfPermission() 方法返回 PERMISSION_DENIED,请调用 shouldShowRequestPermissionRationale()。如果此方法返回 true,请向用户显示指导界面,在此界面中说明用户希望启用的功能为何需要特定权限。(返回true说明应用之前请求过此权限但用户拒绝了请求,此方法将返回 true),之后再调用requestPermissions函数进行权限的请求,这个函数会引起onRequestPermissionsResult的回调。

        private void callPhone(){
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
                    != PackageManager.PERMISSION_GRANTED) {
                // 应用没有授予拨打电话权限,请求权限
                requestPhoneCallPermission();
            } else {
                // 应用被授予拨打电话权限 PackageManager.PERMISSION_GRANTED
                makeCall();
            }
        }
    
    private void requestPhoneCallPermission() {
            // 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)){
                // 向用户详细解释申请该权限的原因
                new AlertDialog.Builder(this)
                        .setCancelable(false)
                        .setMessage("拨打电话需要使用电话权限,如果不授予权限会导致该功能无法正常使用")
                        .setPositiveButton("好的", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                ActivityCompat.requestPermissions(
                                        MainActivity.this,
                                        new String[]{Manifest.permission.CALL_PHONE},
                                        REQUEST_CALLPHONE//注意这个REQUEST_CALLPHONE会传给回调函数的requestCode
                                );
                            }
                        })
                        .setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                            }
                        })
                        .show();
            } else {
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALLPHONE);
            }
        }
    

    下面是回调函数:

    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            if (requestCode == REQUEST_CALLPHONE) {
                if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // 授予权限,拨打电话
                    makeCall();
                } else {
                    Toast.makeText(this, "请求权限被拒绝", Toast.LENGTH_SHORT).show();
                }
            } else {
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    

2、结果展示

当需要打电话的时候第一次请求:

Android权限申请
当点击禁止之后:
Android权限申请
当再一次点击打电话按钮时:
Android权限申请
点击好的(又会看见请求权限的弹窗)
Android权限申请
点击始终允许后,直接把电话打了过去
Android权限申请

三、运行时权限(使用PermissionsDispatcher)

该工具直接使用注解来自动帮我们生成权限请求代码,使用时需要在项目中添加如下两个注解:

    implementation "com.github.permissions-dispatcher:permissionsdispatcher:4.8.0"
    annotationProcessor "com.github.permissions-dispatcher:permissionsdispatcher-processor:4.8.0"
注解 是否必须 作用
@RuntimePermissions 这是必须使用的注解,标记Activity/Fragment,则注解解释器会生成对应类的代码
@NeedsPermission 标记需要请求使用权限的方法,也就是说你获取了相应的权限之后就会执行这个方法,可以在括号里面加一个权限或者多个权限。
@OnShowRationale 对应之前的shouldShowRequestPermissionRationale(),当应用之前请求过此权限但用户拒绝了请求,再次请求时调用,告知用户为什么必须要授权这个权限,注解括号里面有参数,传入想要申请的权限,而且这个方法还要传入一个PermissionRequest对象,这个对象有两种方法:proceed()让权限请求继续,cancel()让请求中断。也就是说,这个方法会拦截你发出的请求,这个方法用于告诉用户你接下来申请的权限是干嘛的,说服用户给你权限。
@OnPermissionDenied 当请求权限遭拒绝时调用,可以告知用户已经拒绝该权限
@OnNeverAskAgain 当用户勾选不再提示,并拒绝权限时,再次请求时调用(如上图中禁止之后不再询问按钮)也就是说,我们可以在这个方法做申请权限失败并选择不再询问之后的处理。例如,可以告诉作者想开启权限的就从手机设置里面开启。

注意:上面这些注解的方法都不能是private,原因看下面
使用PermissionsDispatcher除了要实现注解之外,还要重写Activity的onRequestPermissionsResult()方法,在里面让一个PermissionsDispatcher执行回调。这个PermissionsDispatcher是什么来的呢?

原来只要我们实现了@RuntimePermissions和@NeedsPermission这两个必须的注解之后,再build一次project之后,编译器就会在在app\build\intermediates\classes\debug目录下与被注解的Activity同一个包下生成一个辅助类,名称为 “被注解的Activity的名称+PermissionsDispatcher” 的辅助类,用来调用被注解的Activity的方法(就是因为这个所以被注解的方法不能private,private方法的作用域不在其他的类)。所以,第一次用的话,要注解好之后,build一次,下面的方法里面的PermissionsDispatcherActivityPermissionsDispatcher才不会令AS报红。

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

注意代码中的onclick方法PermissionsDispatcherActivityPermissionsDispatchertakePhotoWithPermissionCheck方法在第一次编译完成之后会自动生成。

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               PermissionsDispatcherActivityPermissionsDispatcher.takePhotoWithPermissionCheck(PermissionsDispatcherActivity.this);
            }
        });

上面的具体代码链接:
https://github.com/Haoocker/Android_Learn/tree/master/RuntimePermissionDemo

参考阅读

  1. https://developer.android.com/guide/topics/permissions/overview?hl=zh-cn
  2. https://www.jianshu.com/p/d6b3e16cc1d9
  3. https://github.com/permissions-dispatcher/PermissionsDispatcher
  4. https://blog.csdn.net/s13383754499/article/details/79034758
上一篇:数禾云上数据湖最佳实践


下一篇:vue实现按钮功能级权限控制