【Java】Android动态权限相关问题梳理

本篇文章主要讲解

1、申请权限为什么需要额外引入appcomat包(或者权限框架)?可以不引入吗?哪些情况可以不用引入?

2、不引入appcompat包的情况下,如何检查是否授权呢?

3、动态权限处理代码参考示例

4、敏感权限组清单及错误理解

5、shouldShowRequestPermissionRationale方法真正含义

6、选择照片和拍照一定要相机权限吗?

Android 6.0开始,对敏感权限需要动态申请,首先我们需要引入android.support.v4兼容包(或appcompat兼容包),然后使用support包中ActivityCompat这个类来处理权限。其实Activity自身也可以处理动态权限,不需要额外引入ActivityCompat。

使用ActivityCompat处理权限需要用到的方法:

ActivityCompat.checkSelfPermission(...)
ActivityCompat.requestPermissions(...)
ActivityCompat.shouldShowRequestPermissionRationale(...)

第一个是检查某个权限是否授权
第二个是申请权限
第三个是判断是否需要合理显示授权对话框。具体含义和用法文末会详解。

Activity自身也可以处理动态权限,也有这些方法,为什么要使用ActivityCompat来处理动态权限?

这也就是为什么要引入兼容包的原因。

第一,如果应用的minSdkVersion小于23(我们一般会设置成15或16左右),直接使用Activity来处理动态权限,那么IDE会提示你这些方法在6.0以下不存在,编译会不通过。

第二,ActivityCompat做了一些兼容性判断,比如requestPermissions的实现里加了判断api 23,在23以下的机器方法不做处理,不弹出对话框。onRequestPermissionsResult回调在23以下是没有的,除非Activity实现了OnRequestPermissionsResultCallback接口。

看看ActivityCompat.requestPermissions函数的源码就明白了:

  public static void requestPermissions(final @NonNull Activity activity,
            final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
        if (sDelegate != null
                && sDelegate.requestPermissions(activity, permissions, requestCode)) {
            // Delegate has handled the permission request.
            return;
        }

        if (Build.VERSION.SDK_INT >= 23) {
            if (activity instanceof RequestPermissionsRequestCodeValidator) {
                ((RequestPermissionsRequestCodeValidator) activity)
                        .validateRequestPermissionsRequestCode(requestCode);
            }
            activity.requestPermissions(permissions, requestCode);
        } else if (activity instanceof OnRequestPermissionsResultCallback) {
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    final int[] grantResults = new int[permissions.length];

                    PackageManager packageManager = activity.getPackageManager();
                    String packageName = activity.getPackageName();

                    final int permissionCount = permissions.length;
                    for (int i = 0; i < permissionCount; i++) {
                        grantResults[i] = packageManager.checkPermission(
                                permissions[i], packageName);
                    }

                    ((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
                            requestCode, permissions, grantResults);
                }
            });
        }
    }

可以看到,如果系统是23以上就调用activity的申请权限方法,如果系统是23以下的,并且activity又没有实现OnRequestPermissionsResultCallback接口,那么这个requestPermissions方法就什么都没做。

所谓兼容包,其实就是帮我们判断了系统版本,针对性地进行处理,防止高版本的api在低版本上不存在而出错。

ActivityCompat.checkSelfPermission函数源码:

  public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
    }

可以看到这个方法其实调用的是Context的方法。需要注意的是,Context的checkPermission方法一直都是存在的,并不是23以后才加入的。

因此如果只是检查权限,不需要使用兼容包,直接调用context的方法即可。

ActivityCompat.shouldShowRequestPermissionRationale函数源码:

  public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
            @NonNull String permission) {
        if (Build.VERSION.SDK_INT >= 23) {
            return activity.shouldShowRequestPermissionRationale(permission);
        }
        return false;
    }
    

shouldShowRequestPermissionRationale其实也是调用了Context的方法。

Android 23以下如何检查权限呢?

上面已经说了,检查权限方法并不是23以后出现的新方法。一种检查方法是使用ActivityCompat,调用checkSelfPermission方法。
另一种方法直接使用context提供的方法:checkPermission(String permission, int pid, int uid),需要传入当前进程id和uid:

context.checkPermission(permission, android.os.Process.myPid(), Process.myUid())
动态权限处理代码示例
package com.devnn.testdemo;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.widget.Toast;

public class MainActivity extends Activity {
    String permission = Manifest.permission.READ_EXTERNAL_STORAGE;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{permission}, 100);
        }else{
            Toast.makeText(this, "已有读SDK卡权限", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode==100){
            if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
                Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();
            }else if(ActivityCompat.shouldShowRequestPermissionRationale(this,permission)==false){
                Toast.makeText(this, "拒绝并勾选了不再提醒,不会弹了,需要引导用户去设置页", Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(this, "拒绝了授权,下次还会再弹", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

记得先要在AndroidManifest.xml里声明权限。

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

在弹出系统的权限对话框前,弹出自已的对话框,用来说明权限用途,会更加友好一点。直接使用Materia风格的Dialog。

    public void showPermissionDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this, android.R.style.Theme_DeviceDefault_Light_Dialog_NoActionBar_MinWidth);
        builder.setMessage("需要读SD卡权限,用来获取照片\n需要获取手机状态权限,用来获取设备号\n需要获取联系人权限,用来...");
        builder.setTitle("权限说明");
        builder.setCancelable(true);
        builder.setPositiveButton("申请", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, 100);
            }
        });
        builder.show();
    }

对话框效果:
在这里插入图片描述

只有危险权限(或叫敏感权限)才需要申请,其它权限不需要申请。

危险权限组:
在这里插入图片描述
要注意的是,“危险(敏感)权限组中的某一个权限授权了,组内其它权限也被同时授权。”这句话是错误的,经过对读写SD卡权限的测试,发现仅申请读SD卡权限后,并没有写权限。当申请了写权限后,读权限自动被授予。

关于Context.shouldShowRequestPermissionRationale(...)方法含义

ActivityCompat.shouldShowRequestPermissionRationale(…)实质也是调用Context的这个方法,参见上文源码。

从字面意思理解,即是否需要合理显示申请权限对话框(注意是系统的不是自定义的)。那么什么时候显示才是合理的呢?经过本人实测发现以下规律:

1、在第一次申请权限前,它返回了false
2、用户选择接受权限后,它返回false
3、用户选择拒绝但是没有勾选不再提醒,它返回true
4、用户选择拒绝并勾选不再提醒(永久拒绝),它返回了false

因此这个方法并不能用来判断是否需要显示自已的权限说明对话框。
它只能在权限回调里用来判断是否要引导用户到设置页打开权限。
此方法正确使用方式参考上文示例代码。

选择照片和拍照一定要相机权限吗?

很多APP有上传照片的需求,比如从用户相册中选择一张照片或者实时拍照上传。其实这种功能我们不需要相机权限,一般我们调用系统相册app即可,大多数相册还自带拍照功能,我们只是打开了别的app,相机权限是别的app自带的。我们的app在不添加任何权限的情况下,可以打开系统相册并且可以选择照片和拍照,但是无法上传,因为你拿不到照片文件。这个时候我们只需要给自己的app添加READ_EXTERNAL_STORAGE的权限即可。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android是一个开放的移动操作系统,爱好者和开发者可以根据自己的需求进行定制和开发。在Android开发中,JavaPoet是一个非常有用的库,可以动态生成Java代码,简化一些重复的工作。而AOP(面向切面编程)则是一种编程范式,可以将横切关注点与业务逻辑分离,提高代码的可复用性和可维护性。 动态权限申请是Android开发中经常遇到的一个问题。在Android系统中,一些敏感的操作和资源访问需要动态申请权限,以确保用户的隐私和安全。传统的权限申请方式是在每个需要权限的地方都进行判断和申请,这样会导致代码的冗余和可读性的下降。使用AOP结合JavaPoet可以实现动态权限申请的解决方案。 首先,我们可以通过AOP在需要权限的方法周围添加一个切面,用于检查和申请权限。通过AspectJ等AOP框架,我们可以定义一个切面,在方法执行之前和之后执行相应的逻辑。 然后,利用JavaPoet动态生成申请权限的代码。我们可以定义一个注解,用于标识需要权限的方法。在AOP切面中,当检测到方法上有该注解时,生成相应的权限申请代码。 最后,在代码编译阶段,通过JavaPoet生成的代码会自动插入到原始代码中。这样,我们就可以在运行时动态地进行权限的申请了。 通过以上的实践,我们可以实现动态权限申请的功能,同时可以减少重复的代码,并提高代码的可维护性。使用JavaPoet和AOP相结合的方式,可以使我们的开发变得更加高效和便捷。它们为Android开发带来了更多的灵活性和扩展性,帮助我们更好地应对权限申请的问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值