Android 动态权限管理学习指南

导读:

Android 6.0版本之前的权限,只要在清单文件中声明就可以了,而Android 6.0 版本以后权限需要动态申请

本篇主要介绍Android 6.0新增的权限机制的简介与使用,用于日常工作快速使用该特性


权限机制简介:

新的权限机制分为两类:Normal Permissions 和 Dangerous Permission

  • Normal Permissions

这类权限直接在清单文件申请即可


ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
  • Dangerous Permission

Android 6.0的权限机制主要针对Dangerous Permission,一共9组,每组中只要有一个被授权,整组授权

可通过 adb shell pm list permissions -d -g 进行查看

group:android.permission-group.CONTACTS
  permission:android.permission.WRITE_CONTACTS
  permission:android.permission.GET_ACCOUNTS
  permission:android.permission.READ_CONTACTS

group:android.permission-group.PHONE
  permission:android.permission.READ_CALL_LOG
  permission:android.permission.READ_PHONE_STATE
  permission:android.permission.CALL_PHONE
  permission:android.permission.WRITE_CALL_LOG
  permission:android.permission.USE_SIP
  permission:android.permission.PROCESS_OUTGOING_CALLS
  permission:com.android.voicemail.permission.ADD_VOICEMAIL

group:android.permission-group.CALENDAR
  permission:android.permission.READ_CALENDAR
  permission:android.permission.WRITE_CALENDAR

group:android.permission-group.CAMERA
  permission:android.permission.CAMERA

group:android.permission-group.SENSORS
  permission:android.permission.BODY_SENSORS

group:android.permission-group.LOCATION
  permission:android.permission.ACCESS_FINE_LOCATION
  permission:android.permission.ACCESS_COARSE_LOCATION

group:android.permission-group.STORAGE
  permission:android.permission.READ_EXTERNAL_STORAGE
  permission:android.permission.WRITE_EXTERNAL_STORAGE

group:android.permission-group.MICROPHONE
  permission:android.permission.RECORD_AUDIO

group:android.permission-group.SMS
  permission:android.permission.READ_SMS
  permission:android.permission.RECEIVE_WAP_PUSH
  permission:android.permission.RECEIVE_MMS
  permission:android.permission.RECEIVE_SMS
  permission:android.permission.SEND_SMS
  permission:android.permission.READ_CELL_BROADCASTS

权限机制基本逻辑:

  1. checkSelfpermission(),如果已经开启,继续执行业务逻辑

  2. 未开启,系统会调用shouldShowReqestPermissionRationale()判断权限是否被彻底禁止

  3. 首次申请或权限被彻底禁止,会调用requestPermisssions()

  4. 最后在Acivity中调用onRequestPermissionResult()拿到结果回调

申请权限步骤(这里以我们封装好的单个权限申请为例):

  1. 在AndroidMainfest.xml文件中声明需要的权限,包括Normal Permissions和Dangerous Permission;

  2. 检查权限:

/**
     * 申请单个权限
     * <p>
     * 部分机型修改过Rom,如小米4,shouldShowRequestPermissionRationale会一直返回false
     *
     * @param activity
     * @param requestCode
     * @param listener
     */
    public static void requestPermission(final Activity activity, final int requestCode, OnPermissionListener listener) {
        mOnPermissionListener = listener;

        if (requestCode < 0 || requestCode >= requestPermissions.length) {
            Logger.i(TAG, "非法的请求码(不是需要动态申请的权限)");
            return;
        }
        final String requestPermission = requestPermissions[requestCode];
        if (ActivityCompat.checkSelfPermission(activity, requestPermission) != PackageManager.PERMISSION_GRANTED) {
            //判断权限是否被彻底禁止,首次调用或彻底禁止调用requestPermissions; 没有彻底禁止调用shouldShowRequestPermissionRationale
            if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) {
                Logger.i(TAG, "是否被彻底禁止: " + !ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission));
                shouldShowRationale(activity, requestCode, requestPermission);
            } else {
                //首次申请权限或彻底禁止,系统调用这个方法
                ActivityCompat.requestPermissions(activity, new String[]{requestPermission}, requestCode);

            }
        } else {
            Logger.i(TAG, "已授权");
            if (mOnPermissionListener != null)
                mOnPermissionListener.onPermissionGranted(requestCode);
        }

    }
  1. 在Activity的onRequestPermissionResult()回调中返回结果处理
//Activity中
/**
     * 权限结果回调
     *
     * @param requestCode  权限的请求码
     * @param permissions  权限数组
     * @param grantResults 授权结果数组
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        PermissionsUtils.onRequestPermissionsResult(this, requestCode, permissions, grantResults);

    }

//工具类处理结果

/**
     * 申请单个权限结果
     *
     * @param activity
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    public static void onRequestPermissionsResult(final Activity activity, final int requestCode, @NonNull String[] permissions,
                                                  @NonNull int[] grantResults) {
        if (requestCode == CODE_ALL_PERMISSION || requestCode == CODE_Mutil_PERMISSION) {
            onRequestMutilPermissionsResult(activity, permissions, grantResults);
            return;
        }
        //不是我们规定的权限
        if (requestCode < 0 || requestCode >= requestPermissions.length) {
            Logger.i(TAG, "非法的请求码:" + requestCode);
            return;
        }

        if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            if (mOnPermissionListener != null)
                mOnPermissionListener.onPermissionGranted(requestCode);

        } else {

            openPermissionsSetting(activity, "系统部分功能需要: " + requestPermissions[requestCode] + "权限,才能正常运作,需要现在手动设置权限吗?");
        }


    }

工具类完整代码(直接拷贝取用就好了)

常量类


package zs.xmx.constant;
/*
 * @创建者     默小铭
 * @博客       http://blog.csdn.net/u012792686
 * @创建时间   2017/9/10
 * @本类描述      权限管理工具类_常量类
 * @使用   import static zs.xmx.constant.PermissionsContants.*; 直接使用
 *
 */

import android.Manifest;
import android.os.Build;

public class PermissionsContants {

    //请求码(2017目前的动态权限,后续有更新继续补充)
    public static final  int CODE_WRITE_CONTACTS         = 0;
    public static final  int CODE_GET_ACCOUNTS           = 1;
    public static final  int CODE_READ_CONTACTS          = 2;
    public static final  int CODE_READ_CALL_LOG          = 3;
    public static final  int CODE_READ_PHONE_STATE       = 4;
    public static final  int CODE_CALL_PHONE             = 5;
    public static final  int CODE_WRITE_CALL_LOG         = 6;
    public static final  int CODE_USE_SIP                = 7;
    public static final  int CODE_PROCESS_OUTGOING_CALLS = 8;
    public static final  int CODE_READ_CALENDAR          = 9;
    public static final  int CODE_WRITE_CALENDAR         = 10;
    public static final  int CODE_CAMERA                 = 11;
    public static final  int CODE_BODY_SENSORS           = 12;
    public static final  int CODE_ACCESS_FINE_LOCATION   = 13;
    public static final  int CODE_ACCESS_COARSE_LOCATION = 14;
    public static final  int CODE_READ_EXTERNAL_STORAGE  = 15;
    public static final  int CODE_WRITE_EXTERNAL_STORAGE = 16;
    public static final  int CODE_RECORD_AUDIO           = 17;
    public static final  int CODE_READ_SMS               = 18;
    public static final  int CODE_RECEIVE_WAP_PUSH       = 19;
    public static final  int CODE_RECEIVE_MMS            = 20;
    public static final  int CODE_RECEIVE_SMS            = 21;
    public static final  int CODE_SEND_SMS               = 22;
    public static final int CODE_ALL_PERMISSION         = 100;
    public static final int CODE_Mutil_PERMISSION       = 200;

    //危险权限9组,25个权限(需要动态申请),每组只要有一个权限申请成功,默认整组权限都能用
    //联系人权限
    public static final String PERMISSION_WRITE_CONTACTS         = Manifest.permission.WRITE_CONTACTS;
    public static final String PERMISSION_GET_ACCOUNTS           = Manifest.permission.GET_ACCOUNTS;
    public static final String PERMISSION_READ_CONTACTS          = Manifest.permission.READ_CONTACTS;
    //电话权限(清单文件没找到PERMISSION_ADD_VOICEMAIL)
    public static final String PERMISSION_READ_CALL_LOG          = Manifest.permission.READ_CALL_LOG;
    public static final String PERMISSION_READ_PHONE_STATE       = Manifest.permission.READ_PHONE_STATE;
    public static final String PERMISSION_CALL_PHONE             = Manifest.permission.CALL_PHONE;
    public static final String PERMISSION_WRITE_CALL_LOG         = Manifest.permission.WRITE_CALL_LOG;
    public static final String PERMISSION_USE_SIP                = Manifest.permission.USE_SIP;
    public static final String PERMISSION_PROCESS_OUTGOING_CALLS = Manifest.permission.PROCESS_OUTGOING_CALLS;
    //public static final String PERMISSION_ADD_VOICEMAIL          = Manifest.permission.ADD_VOICEMAIL;
    //日历权限
    public static final String PERMISSION_READ_CALENDAR          = Manifest.permission.READ_CALENDAR;
    public static final String PERMISSION_WRITE_CALENDAR         = Manifest.permission.WRITE_CALENDAR;
    //相机权限
    public static final String PERMISSION_CAMERA                 = Manifest.permission.CAMERA;
    //传感器权限
    public static final String PERMISSION_BODY_SENSORS           = Manifest.permission.BODY_SENSORS;
    //定位权限
    public static final String PERMISSION_ACCESS_FINE_LOCATION   = Manifest.permission.ACCESS_FINE_LOCATION;
    public static final String PERMISSION_ACCESS_COARSE_LOCATION = Manifest.permission.ACCESS_COARSE_LOCATION;
    //存储权限
    public static final String PERMISSION_READ_EXTERNAL_STORAGE  = Manifest.permission.READ_EXTERNAL_STORAGE;
    public static final String PERMISSION_WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
    //麦克风权限
    public static final String PERMISSION_RECORD_AUDIO           = Manifest.permission.RECORD_AUDIO;
    //短信权限(没找到READ_CELL_BROADCASTS)
    public static final String PERMISSION_READ_SMS               = Manifest.permission.READ_SMS;
    public static final String PERMISSION_RECEIVE_WAP_PUSH       = Manifest.permission.RECEIVE_WAP_PUSH;
    public static final String PERMISSION_RECEIVE_MMS            = Manifest.permission.RECEIVE_MMS;
    public static final String PERMISSION_RECEIVE_SMS            = Manifest.permission.RECEIVE_SMS;
    public static final String PERMISSION_SEND_SMS               = Manifest.permission.SEND_SMS;

    public static final String[] requestPermissions = {
            PERMISSION_WRITE_CONTACTS,
            PERMISSION_GET_ACCOUNTS,
            PERMISSION_READ_CONTACTS,
            PERMISSION_READ_CALL_LOG,
            PERMISSION_READ_PHONE_STATE,
            PERMISSION_CALL_PHONE,
            PERMISSION_WRITE_CALL_LOG,
            PERMISSION_USE_SIP,
            PERMISSION_PROCESS_OUTGOING_CALLS,
            PERMISSION_READ_CALENDAR,
            PERMISSION_WRITE_CALENDAR,
            PERMISSION_CAMERA,
            PERMISSION_BODY_SENSORS,
            PERMISSION_ACCESS_FINE_LOCATION,
            PERMISSION_ACCESS_COARSE_LOCATION,
            PERMISSION_READ_EXTERNAL_STORAGE,
            PERMISSION_WRITE_EXTERNAL_STORAGE,
            PERMISSION_RECORD_AUDIO,
            PERMISSION_READ_SMS,
            PERMISSION_RECEIVE_WAP_PUSH,
            PERMISSION_RECEIVE_MMS,
            PERMISSION_RECEIVE_SMS,
            PERMISSION_SEND_SMS
    };

    public static final String[] CONTACTS_GROUP;//读写联系人
    public static final String[] PHONE_GROUP;//读电话状态、打电话、读写电话记录
    public static final String[] CALENDAR_GROUP;//读写日厉
    public static final String[] CAMERA_GROUP;//相机
    public static final String[] SENSORS_GROUP;//传感器
    public static final String[] LOCATION_GROUP;//读位置信息
    public static final String[] STORAGE_GROUP;//读写存储卡
    public static final String[] MICROPHONE_GROUP;//使用麦克风
    public static final String[] SMS_GROUP;//读写短信、收发短信

    /**
     * 在Android M以前使用某权限是不需要用户授权的,
     * 只要在Manifest中注册即可,在Android M之后需要注册并申请用户授权,
     * 所以我们根据系统版本在Android M以前用一个空数组作为权限组,在Android M以后用真实数组权限。
     */
    static {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            CONTACTS_GROUP = new String[]{};
            PHONE_GROUP = new String[]{};
            CALENDAR_GROUP = new String[]{};
            CAMERA_GROUP = new String[]{};
            SENSORS_GROUP = new String[]{};
            LOCATION_GROUP = new String[]{};
            STORAGE_GROUP = new String[]{};
            MICROPHONE_GROUP = new String[]{};
            SMS_GROUP = new String[]{};
        } else {
            CONTACTS_GROUP = new String[]{
                    PERMISSION_WRITE_CONTACTS,
                    PERMISSION_READ_CONTACTS,
                    PERMISSION_GET_ACCOUNTS
            };
            PHONE_GROUP = new String[]{
                    PERMISSION_READ_CALL_LOG,
                    PERMISSION_READ_PHONE_STATE,
                    PERMISSION_CALL_PHONE,
                    PERMISSION_WRITE_CALL_LOG,
                    PERMISSION_USE_SIP,
                    PERMISSION_PROCESS_OUTGOING_CALLS
            };
            CALENDAR_GROUP = new String[]{
                    PERMISSION_READ_CALENDAR,
                    PERMISSION_WRITE_CALENDAR
            };
            CAMERA_GROUP = new String[]{
                    PERMISSION_CAMERA
            };
            SENSORS_GROUP = new String[]{
                    PERMISSION_BODY_SENSORS
            };
            LOCATION_GROUP = new String[]{
                    PERMISSION_ACCESS_COARSE_LOCATION,
                    PERMISSION_ACCESS_FINE_LOCATION
            };
            STORAGE_GROUP = new String[]{
                    PERMISSION_READ_EXTERNAL_STORAGE,
                    PERMISSION_WRITE_EXTERNAL_STORAGE
            };
            MICROPHONE_GROUP = new String[]{
                    PERMISSION_RECORD_AUDIO
            };
            SMS_GROUP = new String[]{
                    PERMISSION_READ_SMS,
                    PERMISSION_RECEIVE_WAP_PUSH,
                    PERMISSION_RECEIVE_MMS,
                    PERMISSION_RECEIVE_SMS,
                    PERMISSION_SEND_SMS,
            };
        }

    }


}

工具类


package zs.xmx.permission;
/*
 * @创建者     mqm
 * @博客       http://blog.csdn.net/u012792686
 * @创建时间   2017/9/10
 * @本类描述      Android 6.0 动态申请权限工具类
 * @内容说明     1.申请对应权限
 *              2.申请所有权限
 *              3.申请多个权限
 *              4.特殊权限
 *
 *             使用:
 *             1.在清单文件声明权限
 *             2.在对应业务调用申请权限方法
 *                 PermissionsUtils.requestPermission()
 *                 PermissionsUtils.requestMultiPermission(this,new String[]{Manifest.permission.CALL_PHONE},mOnPermissionListener);
 *                 PermissionsUtils.requestALLPermission()
 *             3.在申请权限的页面(Activity:this,Fragment:getActivity或View:getContext),
 *             在onRequestPermissionsResult()调用PermissionsUtils.onRequestPermissionsResult()
 *
 *
 *
 * @补充内容
 *
 *       注意:
 *       1.部分手机修改过安卓系统Rom,如小米4,如小米4,shouldShowRequestPermissionRationale会一直返回false
 *       2.targetSDKVersion>=23才有动态权限机制
 *       3.6.0以前的版本,shouldShowRequestPermissionRationale会一直返回false
 *       4.兼容性,使用V4包下的ActivityCompat(CotextCompat是它的父类)
 *       5.6.0以前的版本,ActivityCompat.checkSelfPermission()会始终等于PERMISSION_GRANTED
 *       6.6.0以前的版本,如果用户关闭了你申请的权限,ActivityCompat.checkSelfPermission(),会导致程序崩溃,加判断低于23就不申请权限
 *       7.如果在清单文件中声明了CAMERA权限,我们使用Intent的方式启动系统相机,会产生SecurityException,针对这些现象,我们需要先判断该权限是否已经授权
 *       8.Android 6.0 增加了对附近设备的权限限制,如下三个API在调用前都需要ACCESS_FINE_LOCATION 或者 ACCESS_COARSE_LOCATION权限
 *          1. WifiManager.getScanResults()
 *          2. BluetoothDevice.ACTION_FOUND
 *          3. BluetoothLeScanner.startScan()
 *
 *
 *
 */

import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import zs.xmx.utils.DialogUtils;
import zs.xmx.utils.Logger;

import static zs.xmx.constant.PermissionsContants.CODE_ALL_PERMISSION;
import static zs.xmx.constant.PermissionsContants.CODE_Mutil_PERMISSION;
import static zs.xmx.constant.PermissionsContants.requestPermissions;

public class PermissionsUtils {


    private static String TAG = "PermissionsUtils";


    private static OnPermissionListener mOnPermissionListener;

    public interface OnPermissionListener {//申请权限回调

        /**
         * 授权成功,把请求码返回(请求码可不处理)
         */
        void onPermissionGranted(int requestCode);

    }

    /**
     * 申请单个权限
     * <p>
     * 只申请权限,不对某个请求的权限返回码作处理
     *
     * @param activity
     * @param requestCode
     */
    public static void requestPermission(final Activity activity, final int requestCode) {
        requestPermission(activity, requestCode, null);

    }


    /**
     * 申请单个权限
     * <p>
     * 部分机型修改过Rom,如小米4,shouldShowRequestPermissionRationale会一直返回false
     *
     * @param activity    上下文
     * @param requestCode 请求码
     * @param listener    监听器
     */
    public static void requestPermission(final Activity activity, final int requestCode, OnPermissionListener listener) {
        mOnPermissionListener = listener;

        if (requestCode < 0 || requestCode >= requestPermissions.length) {
            Logger.i(TAG, "非法的请求码(不是需要动态申请的权限)");
            return;
        }

        final String requestPermission = requestPermissions[requestCode];
        if (ActivityCompat.checkSelfPermission(activity, requestPermission) != PackageManager.PERMISSION_GRANTED) {
            //判断权限是否被彻底禁止,首次调用或彻底禁止调用requestPermissions; 没有彻底禁止调用shouldShowRequestPermissionRationale
            if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) {
                Logger.i(TAG, "是否被彻底禁止: " + !ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission));
                shouldShowRationale(activity, requestCode, requestPermission);
            } else {
                /**
                 * 首次申请权限或彻底禁止,系统调用这个方法
                 *
                 * 第一个参数是Context
                 * 第二个参数是需要申请的权限的字符串数组(由此看出支持一次性申请多个权限)
                 * 第三个参数为requestCode,回调的时候检测
                 *
                 */
                ActivityCompat.requestPermissions(activity, new String[]{requestPermission}, requestCode);

            }
        } else {
            Logger.i(TAG, "已授权");
            if (mOnPermissionListener != null)
                mOnPermissionListener.onPermissionGranted(requestCode);
        }

    }

    /**
     * 申请一组或多组权限
     * <p>
     * 只申请权限,不对某个请求的权限返回码作处理
     *
     * @param activity
     * @param PermissionGroup PermissionsContants.XX_GROUP
     */
    public static void requestPermissionGroup(final Activity activity, final String[]... PermissionGroup) {

        List<String> list = new ArrayList<>();

        for (int i = 0; i < PermissionGroup.length; i++) {
            for (int j = 0; j < PermissionGroup[i].length; j++) {
                list.add(PermissionGroup[i][j]);
            }
        }
        String[] permissions = list.toArray(new String[list.size()]);
        if (permissions.length == list.size()) {
            Log.i(TAG, "requestPermissionGroup: "+list.size());
            requestMultiPermission(activity, permissions, null);
        }
    }

    /**
     * 申请一组或多组权限
     * <p>
     * 只申请权限,不对某个请求的权限返回码作处理
     *
     * @param activity
     * @param PermissionGroup PermissionsContants.XX_GROUP
     */
    public static void requestPermissionGroup(final Activity activity, OnPermissionListener listener, final String[]... PermissionGroup) {

        List<String> list = new ArrayList<>();

        for (int i = 0; i < PermissionGroup.length; i++) {
            for (int j = 0; j < PermissionGroup[i].length; j++) {
                list.add(PermissionGroup[i][j]);
            }
        }

        requestMultiPermission(activity, list.toArray(new String[list.size()]), listener);
    }

    /**
     * 申请多个权限
     * <p>
     * 只申请权限,不对某个请求的权限返回码作处理
     *
     * @param activity
     * @param mutilPermissionList
     */
    public static void requestMultiPermission(final Activity activity, final String[] mutilPermissionList) {
        requestMultiPermission(activity, mutilPermissionList, null);

    }

    /**
     * 申请多个权限
     * <p>
     * 部分机型修改过Rom,如小米4,shouldShowRequestPermissionRationale会一直返回false
     *
     * @param activity
     * @param mutilPermissionList
     * @param listener
     */
    public static void requestMultiPermission(final Activity activity, final String[] mutilPermissionList, OnPermissionListener listener) {
        mOnPermissionListener = listener;
        /**首次申请或彻底禁止,没有申请到的权限**/
        final List<String> permissionsList = getMutilPermission(activity, mutilPermissionList, false);
        /**没有彻底禁止,但没有申请到的权限**/
        final List<String> shouldRationalePermissionsList = getMutilPermission(activity, mutilPermissionList, true);
        if (permissionsList == null || shouldRationalePermissionsList == null) {
            return;
        }
        Logger.i(TAG, "requestMultiPermissions permissionsList:" + permissionsList.size() + ",shouldRationalePermissionsList:" + shouldRationalePermissionsList.size());

        if (permissionsList.size() > 0) {
            for (int i = 0; i < requestPermissions.length; i++) {
                for (int j = 0; j < mutilPermissionList.length; j++) {
                    if (mutilPermissionList[j].equals(requestPermissions[i])) {
                        Logger.i(TAG, mutilPermissionList[j]);
                        ActivityCompat.requestPermissions(activity, permissionsList.toArray(new String[permissionsList.size()]),
                                CODE_Mutil_PERMISSION);
                    }
                }
            }

        } else if (shouldRationalePermissionsList.size() > 0) {
            showMessageOKCancel(activity, "这些权限需要授权",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            ActivityCompat.requestPermissions(activity, shouldRationalePermissionsList.toArray(new String[shouldRationalePermissionsList.size()]),
                                    CODE_Mutil_PERMISSION);
                            Logger.i(TAG, "showMessageOKCancel requestPermissions");
                        }
                    });
        } else {
            Logger.i(TAG, "已授权");
            if (mOnPermissionListener != null)
                mOnPermissionListener.onPermissionGranted(CODE_Mutil_PERMISSION);
        }

    }

    /**
     * 申请全部权限
     * <p>
     * 只申请权限,不对某个请求的权限返回码作处理
     *
     * @param activity
     */
    public static void requestAllPermissions(final Activity activity) {
        requestAllPermissions(activity, null);

    }


    /**
     * 申请全部权限
     * <p>
     * 部分机型修改过Rom,如小米4,shouldShowRequestPermissionRationale会一直返回false
     *
     * @param activity
     */
    public static void requestAllPermissions(final Activity activity, OnPermissionListener listener) {
        mOnPermissionListener = listener;
        //首次申请或彻底禁止,没有申请到的权限
        final List<String> permissionsList = getDeniedPermission(activity, false);
        //没有彻底禁止,但没有申请到的权限
        final List<String> shouldRationalePermissionsList = getDeniedPermission(activity, true);
        if (permissionsList == null || shouldRationalePermissionsList == null) {
            return;
        }
        Logger.i(TAG, "requestMultiPermissions permissionsList:" + permissionsList.size() + ",shouldRationalePermissionsList:" + shouldRationalePermissionsList.size());

        if (permissionsList.size() > 0) {
            ActivityCompat.requestPermissions(activity, permissionsList.toArray(new String[permissionsList.size()]),
                    CODE_ALL_PERMISSION);
        } else if (shouldRationalePermissionsList.size() > 0) {
            showMessageOKCancel(activity, "这些权限需要授权",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            ActivityCompat.requestPermissions(activity, shouldRationalePermissionsList.toArray(new String[shouldRationalePermissionsList.size()]),
                                    CODE_ALL_PERMISSION);
                            Logger.i(TAG, "showMessageOKCancel requestPermissions");
                        }
                    });
        } else {
            Logger.i(TAG, "已授权");
            if (mOnPermissionListener != null)
                mOnPermissionListener.onPermissionGranted(CODE_ALL_PERMISSION);
        }
    }


    private static void showMessageOKCancel(Activity context, String message, DialogInterface.OnClickListener okListener) {
        DialogUtils.showAlert(context, "温馨提示", message, "确定", okListener, "取消", null);
    }

    /**
     * 获取没有申请到的权限
     *
     * @param activity
     * @param isShouldRationale true: return Denied and shouldShowRequestPermissionRationale permissions, false:return Denied and !shouldShowRequestPermissionRationale
     * @return
     */
    private static ArrayList<String> getDeniedPermission(Activity activity, boolean isShouldRationale) {

        ArrayList<String> permissions = new ArrayList<>();

        for (int i = 0; i < requestPermissions.length; i++) {
            String requestPermission = requestPermissions[i];

            if (ActivityCompat.checkSelfPermission(activity, requestPermission) != PackageManager.PERMISSION_GRANTED) {
                if (ActivityCompat.shouldShowRequestPermissionRationale(activity, requestPermission)) {
                    if (isShouldRationale) {
                        permissions.add(requestPermission);
                    }

                } else {
                    if (!isShouldRationale) {
                        permissions.add(requestPermission);
                    }
                }

            }
        }

        return permissions;
    }

    /**
     * 获取要申请的多组权限
     *
     * @param activity
     * @param isShouldRationale true: return Denied and shouldShowRequestPermissionRationale permissions, false:return Denied and !shouldShowRequestPermissionRationale
     * @return
     */
    private static ArrayList<String> getMutilPermission(Activity activity, String[] mutilPermissionList, boolean isShouldRationale) {

        ArrayList<String> permissions = new ArrayList<>();

        for (int i = 0; i < mutilPermissionList.length; i++) {
            String mutilPermission = mutilPermissionList[i];

            if (ActivityCompat.checkSelfPermission(activity, mutilPermission) != PackageManager.PERMISSION_GRANTED) {
                try {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(activity, mutilPermission)) {
                        if (isShouldRationale) {
                            permissions.add(mutilPermission);
                        }

                    } else {
                        if (!isShouldRationale) {
                            permissions.add(mutilPermission);
                        }
                    }
                } catch (Exception e) {
                    Logger.i(TAG, e.getMessage());
                }

            }
        }

        return permissions;
    }

    /**
     * 弹出Dialog向用户解释为何申请权限shouldShowRequestPermissionRationale。
     *
     * @param activity
     * @param requestCode
     * @param requestPermission
     */
    private static void shouldShowRationale(final Activity activity, final int requestCode, final String requestPermission) {
        //申请权限还是调用 ActivityCompat.requestPermissions(activity,new String[]{requestPermission},requestCode);
        showMessageOKCancel(activity, "系统部分功能需要: " + requestPermissions[requestCode] + "权限,才能正常运作,需要现在手动设置权限吗?", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ActivityCompat.requestPermissions(activity,
                        new String[]{requestPermission},
                        requestCode);
            }
        });
    }


    /**
     * 申请单个权限结果
     *
     * @param activity
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    public static void onRequestPermissionsResult(final Activity activity, final int requestCode, @NonNull String[] permissions,
                                                  @NonNull int[] grantResults) {
        if (requestCode == CODE_ALL_PERMISSION || requestCode == CODE_Mutil_PERMISSION) {
            onRequestMutilPermissionsResult(activity, permissions, grantResults);
            return;
        }
        //不是我们规定的权限
        if (requestCode < 0 || requestCode >= requestPermissions.length) {
            Logger.i(TAG, "非法的请求码:" + requestCode);
            return;
        }

        if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            if (mOnPermissionListener != null)
                mOnPermissionListener.onPermissionGranted(requestCode);

        } else {

            openPermissionsSetting(activity, "系统部分功能需要: " + requestPermissions[requestCode] + "权限,才能正常运作,需要现在手动设置权限吗?");
        }


    }

    /**
     * 申请所有(多个)权限结果
     * <p>
     * ps:(多个)所有权限参数基本一致,写到一起了
     *
     * @param activity
     * @param permissions
     * @param grantResults
     */
    private static void onRequestMutilPermissionsResult(Activity activity, String[] permissions, int[] grantResults) {

        Map<String, Integer> perms = new HashMap<>();

        ArrayList<String> notGranted = new ArrayList<>();
        for (int i = 0; i < permissions.length; i++) {
            perms.put(permissions[i], grantResults[i]);
            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                notGranted.add(permissions[i]);
            }
        }

        if (notGranted.size() == 0) {
            Toast.makeText(activity, "权限申请成功" + notGranted, Toast.LENGTH_SHORT).show();
        } else {

            openPermissionsSetting(activity, "部分权限需要手动授权");

        }
    }


    /**
     * 打开系统权限设置页面
     *
     * @param activity
     * @param message
     */

    private static void openPermissionsSetting(final Activity activity, String message) {
        showMessageOKCancel(activity, message, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                Logger.i(TAG, "getPackageName(): " + activity.getPackageName());
                Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
                intent.setData(uri);
                activity.startActivity(intent);
            }
        });
    }

    /**
     * 判断清单文件是否声明了某项权限的方法
     *
     * @param context
     * @param permissionName
     * @return
     */
    public boolean hasPermissionInManifest(Context context, String permissionName) {
        final String packageName = context.getPackageName();
        try {
            final PackageInfo packageInfo = context.getPackageManager()
                    .getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
            final String[] declaredPermisisons = packageInfo.requestedPermissions;
            if (declaredPermisisons != null && declaredPermisisons.length > 0) {
                for (String p : declaredPermisisons) {
                    if (p.equals(permissionName)) {
                        return true;
                    }
                }
            }
        } catch (PackageManager.NameNotFoundException e) {

        }
        return false;
    }


}

两个特殊权限

特殊权限,顾名思义,就是一些特别敏感的权限,在Android系统中,主要有两个:

  • SYSTEM_ALERT_WINDOW(设置悬浮窗,显示在其他所有程序的顶层)

  • WRITE_SETTINGS(允许应用程序更改主屏幕中的设置和快捷方式)

关于上面两个特殊权限的授权有点特殊,做法是使用startActivityForResult启动授权界面来完成。

//清单文件先声明权限,在Activty中:

    /**
     * 悬浮窗权限
     * <p>
     * 引导用用户去设置页面设置
     * <p>
     * <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
     * <p>
     * 使用Action Settings.ACTION_MANAGE_OVERLAY_PERMISSION启动隐式Intent
     * <p>
     * 使用"package:" + getPackageName()携带App的包名信息
     * <p>
     * 使用Settings.canDrawOverlays方法判断授权结果
     *
     * @param view
     */
    public void Floating_window(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // 判断是否有SYSTEM_ALERT_WINDOW权限
            if (!Settings.canDrawOverlays(this)) {
                // 申请SYSTEM_ALERT_WINDOW权限
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                intent.setData(Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, REQUEST_Floating_WINDOW);
            } else {
                //doSomething
            }
        }
    }

    /**
     * 系统设置
     * <p>
     * 引导用用户去设置页面设置
     * <p>
     * <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
     * <p>
     * 使用Action Settings.ACTION_MANAGE_WRITE_SETTINGS 启动隐式Intent
     * <p>
     * 使用"package:" + getPackageName()携带App的包名信息
     * <p>
     * 使用Settings.System.canWrite方法检测授权结果
     *
     * @param view
     */
    public void System_Setting(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //判断是否有WRITE_SETTINGS权限
            if (!Settings.System.canWrite(this)) {
                //申请WRITE_SETTINGS权限
                Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
                intent.setData(Uri.parse("package:" + getPackageName()));
                startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS);
            } else {
                //doSomething
            }
        }

    }

    private static final int REQUEST_Floating_WINDOW     = 1;
    private static final int REQUEST_CODE_WRITE_SETTINGS = 2;

    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_Floating_WINDOW) {
            if (Settings.canDrawOverlays(this)) {
                Logger.i(TAG, "onActivityResult granted");
            }
        }
        if (requestCode == REQUEST_CODE_WRITE_SETTINGS) {
            if (Settings.System.canWrite(this)) {
                Logger.i(TAG, "onActivityResult write settings granted");
            }
        }
    }

==爬坑==

  1. 为了保持兼容,使用v4包的兼容性方法:(ContextCompat是它的父类)

ActivityCompat.checkSelfPermission()

ActivityCompat.requestPermissions()

ActivityCompat.OnRequestPermissionsResultCallback()

ActivityCompat.shouldShowRequestPermissionRationale()

  1. 当targetSdkVersion小于23,但是设备是6.0系统时
    设备权限模型使用老的版本,清单文件中列出的权限只会在安装时询问用户可以在设置列表中编辑相关权限,这对应用能否正常运行有很大影响

  2. 当targetSdkVersion大于等于23,但是设备系统小于6.0时
    设备权限模型使用老的版本,清单文件中列出的权限只会在安装时询问

  3. 部分手机修改过安卓系统Rom,如小米4,shouldShowRequestPermissionRationale会一直返回false

  4. 6.0以前的版本,ActivityCompat.checkSelfPermission()会始终等于PERMISSION_GRANTED

  5. 6.0以前的版本,如果用户关闭了你申请的权限,使用ActivityCompat.checkSelfPermission(),会导致程序崩溃,需要加判断低于23就不申请权限

  6. 第一次用户拒绝权限,为例提高用户体验,应友好提示用户不适用该权限无法使用某些功能,添加手动授权设置

  7. shouldShowRequestPermissionRationale这个API可以帮我们判断接下来的对话框是否包含”不再询问”选择框.在6.0之前的版本永远返回的是fasle

  8. 定位权限需要手机先开启位置服务,否则会获取不到

  9. Android 6.0 增加了对附近设备的权限限制(蓝牙设备搜索,附近wifi扫描),如下三个API在调用前都需要ACCESS_FINE_LOCATION 或者 ACCESS_COARSE_LOCATION权限,不然获取不到附近设备信息
    1.WifiManager.getScanResults()
    2.BluetoothDevice.ACTION_FOUND
    3.BluetoothLeScanner.startScan()

  10. 如果在清单文件中声明了CAMERA权限,我们使用Intent的方式启动系统相机,会产生SecurityException,针对这些现象,我们需要先判断该权限是否已经授权

  11. 部分第三方SDK还没兼容到Android 6.0的,我们可以设置targetSdkVersion 低于23


Android Studio plugins 插件方法:

使用:

  1. 在moulde的build.gradle文件下添加以下代码;

dependencies {

compile ('com.github.hotchemi:permissionsdispatcher:2.4.0'){
        exclude module: "support-v13"
    }
    annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:2.4.0'
}
  1. 在Android Studio中搜索并下载PermissionsDispatcher
    File->setting->plugins->PermissionsDispatcher plugin

  2. 在清单文件声明要申请的权限

  3. 设置参数:在要动态获取权限的页面,如MainActivity->右键->Generate->Generate Runtime Permissions…

这里写图片描述

@RuntimePermissions (必选)
- 注释此活动或碎片

@NeedsPermission (必选)
- 当进入Activity类时,如果用户已授权权限,会直接调用该注解方法

@OnShowRationale (可选)
- 当第一次申请权限时,用户选择拒绝,再次申请时调用此方法.即权限没有被彻底禁止时调用

@OnPermissionDenied (可选)
- 当申请的权限被用户拒绝后,调用此方法

@OnNeverAskAgain (可选)
- 当用户点击”不再询问”后,调用此方法.

  1. 稍等片刻,编译器会在app\build\intermediates\classes\debug\包名\目录下与被注解的Activity同一个包下生成一个辅助类,名称为被注解的Activity名称+PermissionsDispatcher.class的代理类
        //代理类检查权限(在要检查权限的业务里调用这个方法)
        PermissionsActivityPermissionsDispatcher.getCameraWithCheck(this);

插件方法事例代码:

package com.example.administrator.myapplication;


import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;

import permissions.dispatcher.NeedsPermission;
import permissions.dispatcher.OnNeverAskAgain;
import permissions.dispatcher.OnPermissionDenied;
import permissions.dispatcher.OnShowRationale;
import permissions.dispatcher.PermissionRequest;
import permissions.dispatcher.RuntimePermissions;


/**
 * PS:
 * 1,执行request.proceed()调用系统申请权限的弹窗;
 * 如果在系统申请弹窗中勾选了不在提示并且拒绝,会调用@OnNeverAskAgain的方法;
 * <p>
 * 2,执行request.cancel()会调用@OnPermissionDenied的方法。
 */

@RuntimePermissions
public class PermissionsActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permissions);
        //代理类检查权限(在要检查权限的业务里调用这个方法)
        PermissionsActivityPermissionsDispatcher.getCameraWithCheck(this);
    }


    @NeedsPermission(Manifest.permission.CAMERA)
    void getCamera() {
    //权限申请成功,在这里写逻辑
    }

    /**
     * 权限回调处理
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //权限回调结果交由PermissionsActivityPermissionsDispatcher处理
        PermissionsActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
    }

    @OnShowRationale(Manifest.permission.CAMERA)
    public void showRationaleForCamera(final PermissionRequest request) {
        new AlertDialog.Builder(this)
                .setCancelable(false)
                .setTitle("权限申请")
                .setMessage("应用需要使用相机权限,您是否确定要使用")
                .setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        request.cancel();
                    }
                })
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        request.proceed();
                    }
                })
                .setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialog) {

                    }
                })
                .show();
    }

    @OnPermissionDenied(Manifest.permission.CAMERA)
    public void showDeniedForCamera() {
        new AlertDialog.Builder(this)
                .setCancelable(false)
                .setTitle("权限申请")
                .setMessage("在设置-应用-当前应用权限中开启相机权限,以正常使用拍照")
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        finish();
                    }
                })
                .setPositiveButton("去设置", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //开启设置页
                        startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
                        dialog.dismiss();
                    }
                })
                .show();
    }

    @OnNeverAskAgain(Manifest.permission.CAMERA)
    public void showNeverAskForCamera() {
        new AlertDialog.Builder(this)
                .setCancelable(false)
                .setTitle("权限申请")
                .setMessage("您已禁止不再询问,请前往设置-应用-当前应用权限中开启相机权限,以正常使用拍照")
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        finish();
                    }
                })
                .setPositiveButton("去设置", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //开启设置页
                        startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
                        dialog.dismiss();
                    }
                })
                .show();
    }
}

特殊权限插件方法:


//代码生成方法和普通方法一样,代理类调用方法不同,如下调用:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    findViewById(R.id.button_system_alert_window).setOnClickListener(v -> {
      // NOTE: delegate the permission handling to generated method
      MainActivityPermissionsDispatcher.systemAlertWindowWithCheck(this);
    });
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    MainActivityPermissionsDispatcher.onActivityResult(this, requestCode);
}

效果图:

这里写图片描述

==注意==

  1. 执行request.proceed()调用系统申请权限的弹窗;
    如果在系统申请弹窗中勾选了不在提示并且拒绝,会调用@OnNeverAskAgain的方法;

  2. 执行request.cancel()会调用@OnPermissionDenied的方法.


总结:

该工具类会不断改进,后面会做更多的测试,也欢迎大家评论,提出意见,共同进步

如果我的文章确实给您带来帮助,欢迎关注,后续会不断更新文章的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值