Android 危险权限使用申请 工具类 封装与简便使用

背景

危险权限的申请使用是现如今app开发必备的一部分内容,这里就简单的做一下记录,方便自己后期使用,可以快速的应用到项目中去。

为什么需要危险权限?

  • 回答:当需要使用危险权限时才做申请并等待用户应答,不需要使用时用户可以自由的关闭这些危险权限,等到用到相关功能时才继续向用户申请危险权限的使用。危险权限顾名思义拥有就很危险,所以使用时才打开,后面可以自己自由的控制危险权限的开启,这样才是一个正确的权限使用规则。

目前来说,基本上刚进入app后都会将一些用到的危险权限都统一的进行一次性申请使用,等待用户操作完后再进入到app中,如果用户拒绝了这些危险权限的使用,那么也没关系,我们可以等用户使用需要相关权限才能用的功能时再向用户申请危险权限的一个使用。


危险权限整理

这里就不重复造文字了,很多现有的文章都很详细

Android 中的危险权限详细整理


工具类与接口

涉及一个工具类和一个接口

工具类PVerifyUtil.java的代码如下:

package com.example.pverificationdemo.util;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;

import com.example.pverificationdemo.R;
import com.example.pverificationdemo.interfaces.GrantListener;

/**
 * 危险权限 申请使用 工具类
 * 目前是 9组24个
 */
public class PVerifyUtil {

    public static final int CAMERA = 0x1001;//相机

    public static final int READ_CALENDAR = 0x1002;// 日历
    public static final int WRITE_CALENDAR = 0x1003;

    public static final int READ_CONTACTS = 0x1004;// 联系人
    public static final int WRITE_CONTACTS = 0x1005;
    public static final int GET_ACCOUNTS = 0x1006;

    public static final int ACCESS_FINE_LOCATION = 0x1007;// 位置
    public static final int ACCESS_COARSE_LOCATION = 0x1008;

    public static final int RECORD_AUDIO = 0x1009;// 麦克风

    public static final int READ_PHONE_STATE = 0x1010;// 手机
    public static final int CALL_PHONE = 0x1011;
    public static final int READ_CALL_LOG = 0x1012;
    public static final int WRITE_CALL_LOG = 0x1013;
    public static final int ADD_VOICEMAIL = 0x1014;
    public static final int USE_SIP = 0x1015;
    public static final int PROCESS_OUTGOING_CALLS = 0x1016;

    public static final int BODY_SENSORS = 0x1017;// 传感器

    public static final int SEND_SMS = 0x1018;// 短信
    public static final int RECEIVE_SMS = 0x1019;
    public static final int READ_SMS = 0x1020;
    public static final int RECEIVE_WAP_PUSH = 0x1021;
    public static final int RECEIVE_MMS = 0x1022;

    public static final int READ_EXTERNAL_STORAGE = 0x1023;// 存储卡
    public static final int WRITE_EXTERNAL_STORAGE = 0x1024;

    public static final int ONCE_TIME_APPLY = 0x2000;

    private String GROUP_NAME = "";// 声明该变量用来保存危险权限组名

    private GrantListener grantListener = null;

    private Context context;
    private Fragment fragment;

    public PVerifyUtil(Context context, Fragment fragment) {
        this.context = context;
        this.fragment = fragment;
    }

    /**
     * 申请危险权限
     * @param requestCode 请求码
     */
    public void apply(int requestCode, GrantListener grantListener) {

        this.grantListener = grantListener;// 权限同意的监听设置
        String permission = "";
        permission = getPermission(requestCode);
        if (TextUtils.isEmpty(permission)){
            // 未获取到对应危险权限,一般是请求码不按照给定变量设置或请求的不是危险权限时出现
            return;
        }
        // 危险权限申请使用 适配6.0及以上版本
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M){
            if (fragment != null){
                fragment.requestPermissions(new String[]{permission}, requestCode);
            }else {
                ((Activity)context).requestPermissions(new String[]{permission}, requestCode);
            }

        }

    }

    /**
     * 根据请求码获取对应危险权限    同时赋值对应的权限组名
     * @param requestCode
     * @return
     */
    private String getPermission(int requestCode) {
        String permission = "";
        switch (requestCode){
            // 相机
            case CAMERA: permission = Manifest.permission.CAMERA;
                GROUP_NAME = "相机";
                break;
            // 日历
            case READ_CALENDAR: permission = Manifest.permission.READ_CALENDAR;
                GROUP_NAME = "日历";
                break;
            case WRITE_CALENDAR: permission = Manifest.permission.WRITE_CALENDAR;
                GROUP_NAME = "日历";
                break;
            // 联系人
            case READ_CONTACTS: permission = Manifest.permission.READ_CONTACTS;
                GROUP_NAME = "联系人";
                break;
            case WRITE_CONTACTS: permission = Manifest.permission.WRITE_CONTACTS;
                GROUP_NAME = "联系人";
                break;
            case GET_ACCOUNTS: permission = Manifest.permission.GET_ACCOUNTS;
                GROUP_NAME = "联系人";
                break;
            // 位置
            case ACCESS_FINE_LOCATION: permission = Manifest.permission.ACCESS_FINE_LOCATION;
                GROUP_NAME = "位置";
                break;
            case ACCESS_COARSE_LOCATION: permission = Manifest.permission.ACCESS_COARSE_LOCATION;
                GROUP_NAME = "位置";
                break;
            // 麦克风
            case RECORD_AUDIO: permission = Manifest.permission.RECORD_AUDIO;
                GROUP_NAME = "麦克风";
                break;
            // 手机
            case READ_PHONE_STATE: permission = Manifest.permission.READ_PHONE_STATE;
                GROUP_NAME = "手机";
                break;
            case CALL_PHONE: permission = Manifest.permission.CALL_PHONE;
                GROUP_NAME = "手机";
                break;
            case READ_CALL_LOG: permission = Manifest.permission.READ_CALL_LOG;
                GROUP_NAME = "手机";
                break;
            case WRITE_CALL_LOG: permission = Manifest.permission.WRITE_CALL_LOG;
                GROUP_NAME = "手机";
                break;
            case ADD_VOICEMAIL: permission = Manifest.permission.ADD_VOICEMAIL;
                GROUP_NAME = "手机";
                break;
            case USE_SIP: permission = Manifest.permission.USE_SIP;
                GROUP_NAME = "手机";
                break;
            case PROCESS_OUTGOING_CALLS: permission = Manifest.permission.PROCESS_OUTGOING_CALLS;
                GROUP_NAME = "手机";
                break;
            // 传感器
            case BODY_SENSORS: permission = Manifest.permission.BODY_SENSORS;
                GROUP_NAME = "传感器";
                break;
            // 短信
            case SEND_SMS: permission = Manifest.permission.SEND_SMS;
                GROUP_NAME = "短信";
                break;
            case RECEIVE_SMS: permission = Manifest.permission.RECEIVE_SMS;
                GROUP_NAME = "短信";
                break;
            case READ_SMS: permission = Manifest.permission.READ_SMS;
                GROUP_NAME = "短信";
                break;
            case RECEIVE_WAP_PUSH: permission = Manifest.permission.RECEIVE_WAP_PUSH;
                GROUP_NAME = "短信";
                break;
            case RECEIVE_MMS: permission = Manifest.permission.RECEIVE_MMS;
                GROUP_NAME = "短信";
                break;
            // 存储卡
            case READ_EXTERNAL_STORAGE: permission = Manifest.permission.READ_EXTERNAL_STORAGE;
                GROUP_NAME = "存储卡";
                break;
            case WRITE_EXTERNAL_STORAGE: permission = Manifest.permission.WRITE_EXTERNAL_STORAGE;
                GROUP_NAME = "存储卡";
                break;
        }
        return permission;
    }

    /**
     * 用户是否同意权限
     * @param grantResults
     * @return 返回 true 表示用户已同意了,false 则反之
     */
    private boolean isGranted(int[] grantResults) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        return false;
    }

    /**
     * 请求使用某个权限 结果返回
     * @param requestCode
     * @param grantResults
     */
    public void singleApplyResult(int requestCode, @NonNull int[] grantResults) {
        if(isGranted(grantResults)){
            // 权限同意
            if (grantListener != null){
                grantListener.onAgree();
            }
        }else {
            // 未同意 分两种情况
            if (ActivityCompat.shouldShowRequestPermissionRationale(((Activity)context),
                    getPermission(requestCode))){
                // 情况一  用户只拒绝,未选不再提示项
                grantListener.onDeny();
            }else {
                // 情况二  用户拒绝了,并选择了不再提示项 这时候可跳转到应用详情页让用户手动选择打开相关权限操作
                grantListener.onDenyNotAskAgain();
            }

        }
    }

    /**
     * 一次性同时申请所有的权限 结果返回
     * @param permissions
     * @param grantResults
     */
    public void onceTimeApplyResult(String[] permissions, int[] grantResults){
        for (int i = 0;i < permissions.length;i++){
            String permission = permissions[i];// 某个权限

            if (grantResults.length > 0 && grantResults[i] == PackageManager.PERMISSION_GRANTED){

            }else {
                if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context,
                        permission)){

                }else {
                    new AlertDialog.Builder(context)
                            .setTitle("应用详情页跳转")
                            .setIcon(R.mipmap.app_icon_round)
                            .setMessage("请手动开启相应的 <" + getPermissionGroupName(permission) + "> 权限,否则无法使用相关功能")
                            .setCancelable(false)
                            .setPositiveButton("我知道了", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    dialog.dismiss();
                                }
                            }).create().show();
                }
            }
        }

    }

    /**
     * 根据某权限获取权限组名
     * @param permission
     * @return
     */
    private String getPermissionGroupName(String permission) {

        String groupName = "";
        switch (permission){
            case Manifest.permission.READ_CALENDAR:
            case Manifest.permission.WRITE_CALENDAR:
                groupName = "日历";
                break;
            case Manifest.permission.CAMERA:
                groupName = "相机";
                break;
            case Manifest.permission.READ_CONTACTS:
            case Manifest.permission.WRITE_CONTACTS:
            case Manifest.permission.GET_ACCOUNTS:
                groupName = "联系人";
                break;
            case Manifest.permission.ACCESS_FINE_LOCATION:
            case Manifest.permission.ACCESS_COARSE_LOCATION:
                groupName = "位置";
                break;
            case Manifest.permission.RECORD_AUDIO:
                groupName = "麦克风";
                break;
            case Manifest.permission.READ_PHONE_STATE:
            case Manifest.permission.CALL_PHONE:
            case Manifest.permission.READ_CALL_LOG:
            case Manifest.permission.WRITE_CALL_LOG:
            case Manifest.permission.ADD_VOICEMAIL:
            case Manifest.permission.USE_SIP:
            case Manifest.permission.PROCESS_OUTGOING_CALLS:
                groupName = "手机";
                break;
            case Manifest.permission.BODY_SENSORS:
                groupName = "传感器";
                break;
            case Manifest.permission.SEND_SMS:
            case Manifest.permission.RECEIVE_SMS:
            case Manifest.permission.READ_SMS:
            case Manifest.permission.RECEIVE_WAP_PUSH:
            case Manifest.permission.RECEIVE_MMS:
                groupName = "短信";
                break;
            case Manifest.permission.READ_EXTERNAL_STORAGE:
            case Manifest.permission.WRITE_EXTERNAL_STORAGE:
                groupName = "存储";
                break;
        }
        return groupName;
    }

}

工具类整体来说代码比较简单,注释详细,里面有另外用到一个GrantListener接口,接口代码如下:

package com.example.pverificationdemo.interfaces;

public interface GrantListener {

    void onAgree();// 某权限同意

    void onDeny();// 某权限拒绝但没选择不再询问项

    void onDenyNotAskAgain();// 某权限拒绝并选择了不再询问项
}

这个接口的作用分别对应用户的三种选择操作。

当用户拒绝某一危险权限后app因为相关功能需要继续向用户申请危险权限的使用,这时候会出现下面的弹框:

在这里插入图片描述

这里就很明显了,接口中的三个方法调用时机就分别跟这三个选项的选择相关联。


使用

这里假定同学们把上面的接口和工具类都添加到项目中去了,然后继续下面的操作。

Activity中单个危险权限申请

  • 声明与创建PVerifyUtil对象:
private PVerifyUtil pVerifyUtil;

pVerifyUtil = new PVerifyUtil(this, null);

注意:在activity中使用,对象创建的第二个参数固定传值null

  • 请求单个权限的使用:
// 请求使用单个权限
                pVerifyUtil.apply(PVerifyUtil.CAMERA, new GrantListener() {
                    @Override
                    public void onAgree() {
                        Toast.makeText(MainActivity.this, "相机权限已同意", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onDeny() {
                        Toast.makeText(MainActivity.this, "相机权限被拒绝,未点击不再询问", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onDenyNotAskAgain() {
                        Toast.makeText(MainActivity.this, "相机权限被拒绝并点击了不再询问", Toast.LENGTH_SHORT).show();
                    }
                });
/**
     * 请求权限结果回调
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch (requestCode){

            case PVerifyUtil.CAMERA:
                pVerifyUtil.singleApplyResult(requestCode, grantResults);
                break;

        }
    }

注意:这里单个危险权限申请使用的时候用的是 PVerifyUtil.CAMERA,那么在回调中也要使用 PVerifyUtil.CAMERA与之对应

另外注意记得在清单配置文件中也要声明要使用的危险权限

其他的危险权限都跟上面的请求代码一样,只是修改apply(int requestCode, GrantListener grantListener)方法的第一个参数值即可即可。

申请相机权限第一次拒绝,第二次同意的效果图示如下:

在这里插入图片描述


Activity中多个危险权限一次性申请

  • 示例代码如下:
				// 一次性请求使用多个权限
				
                // 声明所有需要一次性申请的危险权限
                String[] permissions = new String[]{Manifest.permission.CAMERA,
                Manifest.permission.READ_EXTERNAL_STORAGE};
                // 申请时的请求码固定传值 PVerifyUtil.ONCE_TIME_APPLY
                ActivityCompat.requestPermissions(MainActivity.this, permissions,
                        PVerifyUtil.ONCE_TIME_APPLY);


	/**
     * 请求权限结果回调
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch (requestCode){

            case PVerifyUtil.ONCE_TIME_APPLY://一次性请求使用多个权限 结果返回处理
                pVerifyUtil.onceTimeApplyResult(permissions, grantResults);
                break;
         

        }
    }

注意:在结果回调方法onRequestPermissionsResult()中,调用工具类的onceTimeApplyResult(int requestCode, @NonNull int[] grantResults)方法来实现多个请求的逻辑

第一次拒绝,第二次允许权限申请的效果图示如下:

在这里插入图片描述


Fragment 中单个危险权限申请

  • 声明与创建工具类对象
		// 创建工具类对象
        pVerifyUtil = new PVerifyUtil(getContext(), this);

注意:创建对象的第二个参数传值当前Fragment

  • 请求代码如下:
				// 请求使用单个权限
                pVerifyUtil.apply(PVerifyUtil.CAMERA, new GrantListener() {
                    @Override
                    public void onAgree() {
                        Toast.makeText(getContext(), "相机权限已同意", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onDeny() {
                        Toast.makeText(getContext(), "相机权限被拒绝但未点击不再询问", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onDenyNotAskAgain() {
                        Toast.makeText(getContext(), "相机权限被拒绝并点击过不再询问", Toast.LENGTH_SHORT).show();
                    }
                });


	/**
     * 请求权限结果回调
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch (requestCode){
            case PVerifyUtil.CAMERA:// 相机权限
                pVerifyUtil.singleApplyResult(requestCode, grantResults);
                break;

        }
    }

第一次拒绝,第二次同意权限申请的效果图示如下:

在这里插入图片描述


Fragment中多个危险权限一次性申请

示例代码如下:

				// 一次性请求使用多个权限
                // 声明所有需要一次性申请的危险权限
                String[] permissions = new String[]{Manifest.permission.CAMERA,
                        Manifest.permission.READ_EXTERNAL_STORAGE};
                // 申请时的请求码固定传值 PVerifyUtil.ONCE_TIME_APPLY
                requestPermissions( permissions, PVerifyUtil.ONCE_TIME_APPLY);


	/**
     * 请求权限结果回调
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        switch (requestCode){

            case PVerifyUtil.ONCE_TIME_APPLY://一次性请求使用多个权限 结果返回处理
                pVerifyUtil.onceTimeApplyResult(permissions, grantResults);
                break;

        }
    }

注意:在Fragment中的一次性申请跟在Activity中的一次性申请的代码有些许差异。

第一次拒绝,第二次允许权限申请的效果图示如下:

在这里插入图片描述


子Fragment里运行时权限申请时的特殊处理

安卓6.0新特性在Fragment申请运行时权限


技术永不眠,我们下期见!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章