权限-动态申请的权限


申明:低级码农问题解决中的参考和解决后的小结,仅用于个人记录,能力有限,可能有些错误,缺陷不自知,欢迎在评论中指正,谢谢!

从Android 6.0(M版本,api 23)开始,要求动态申请权限。Google将权限分为两类,一类是普通权限,使用时不需要用户授权;另一类是危险权限,使用时需要判断用户是否对该权限授权。如果没有授权,需要动态申请。在没有用户授权的情况下,直接使用该权限,系统将会抛出异常。我们一般说的应用需要某权限,实际是指用户是否同意应用使用该权限。

  危险权限有25种,按照权限组可划分如下,:

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        GPS定位
  permission:android.permission.ACCESS_COARSE_LOCATION        wifi,基站定位

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

  如果应用申请某一种权限,得到用户同意后,那么同组的其他权限,还需要重新申请并同意。拒绝了,也不印象对其他权限的同意。(在meizu X8验证)

  使用权限,需要在AndroidManifest中注册。如果没有注册,那么判断权限的方法将返回DENIED,如果去申请该项权限,也不会有权限对话框弹出。

 

常用的方法

  /** 判断是否具有某一项权限 */

  int checkSelfPermission(String permission)

  /** 申请多个权限 */

  void requestPermissions(@NonNull String[] permissions, int requestCode)

  /** 返回申请的权限的授权情况 */

  void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)

  申请和返回授权中的参数requestCode是对应的,这是用来区分权限申请的场景的。一般情况下,一个Activity只有一种申请单一权限的场景,但也有一个Activity有多个申请单一权限的场景。比如Activity具有多个Fragment,B、C都需要定位权限,在Fragment中检查和申请权限更好一些,但只能有Activity接收授权情况。所以B、C申请时,传入不同的requestCode,返回授权结果方法时,可以根据requestCode判断,是哪个Fragment申请的权限。还有BrowserActivity,不同网页申请同意权限的情况更多了,如果某个网页需要特殊处理,也是用requestCode区分。

    protected static boolean checkRequestMultiPermissions(Activity activity, String... permissions) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            ArrayList<String> deniedPermissions = new ArrayList<>();
            // 过滤出需要但是没有得到授权的权限,放入deniedPermissions
            for(String permission: permissions) {
                int permissionState = activity.checkSelfPermission(permission);
                if (permissionState == PackageManager.PERMISSION_DENIED) {
                    deniedPermissions.add(permission);
                }
            }
            int deniedPermissionCount = deniedPermissions.size();
            if (deniedPermissionCount == 0) {
                return true;
            } else {
                String[] deniedPermissionArray =  deniedPermissions.toArray(new String[0]);
                int deniedPermissionCode = 0;
                for (String permission: deniedPermissionArray) {
                    deniedPermissionCode |= permission2CodeMap.get(permission).intValue();
                }
                activity.requestPermissions(deniedPermissionArray, deniedPermissionCode);
                return false;
            }
        } else {
            return true;
        }
    }

    /** 这个方法运行与Activity中,用户接收授权情况 */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        ArrayList<String> deniedPermissions = new ArrayList<>();
        for (int i = 0, size = permissions.length; i < size; i++) {
            if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                deniedPermissions.add(permissions[i]);
            }
        }
        if (deniedPermissions.size() == 0) {
            // 所有权限都被同意
        } else {
            // deniedPermissions是申请但是不被授权的权限
        }
        return;
    } 

个人心得

  关于requestCode,以我在实际项目中遇到的,会定义很多魔鬼数字权限码,不便于项目的阅读和维护。比如两个Activity都需要定位权限,两个Activity各自定义了权限码,还是不同的数字;然后就是同一个页面需要多个权限(比如应用启动需要存储、IMEI、传感器等)的组合,可能产生很多种requestCode,多人维护的项目不敢乱动代码,导致在不同Activity可以复用的requestCode被定义了多个。所有我有一个方案,用于优化无意义,无限制的定义requestCode,虽不能根本解决,但可以极大程度的缓解。这个方案是为每个权限(指需要动态申请的危险权限,后同)申明一个对应的数字,通过做位运算组合成多种权限的场景,这就要求数字必须是2的n次方形式,即0b10,0b100,0b1000的形式。然后申请权限时,只需要传入权限字符串即可,会生成对应的权限码。

优势,不足和对不足的规避方案。

1、requestCode是int型,取值范围是-2^32 ~ 2^32 - 1,除去负数,然后0表示没有权限,则实际可用的只有30位,而危险权限有25种,不排除后续继续增加,所以用来表示的场景有2^5。因为场景不可能出现组合的情况,所以增加方式是+1,而不是位移(<)。

——适用于我自己的小项目,2^5种场景基本够用了。再就是diy的小项目肯定不会用到全部的权限,给16位到24位就满足绝大多数场景了。

 2、针对Activity定义场景,在onRequestPermissionsResult方法中,尽量走默认场景的统一处理:权限被禁则弹框。如果有特殊处理,就要先判断权限,后判断场景,但这个过程跟Activity强相关,即只有极少数Activity有这样的场景。

  下面是我场景的代码,这是Utils类:

public class PermissionUtils {
    /** 权限组下有多个权限,一个权限被获取了,则改组的所有权限都获取。有25个危险权限,我们定义24个,忽略一个,留个小坑 */
    public static final int PERMISSION_CODE_CALENDAR = 0b1;
    public static final int PERMISSION_CODE_CAMERA = PERMISSION_CODE_CALENDAR << 1;
    public static final int PERMISSION_CODE_CONTACTS = PERMISSION_CODE_CAMERA << 1;
    public static final int PERMISSION_CODE_LOCATION = PERMISSION_CODE_CONTACTS << 1;
    public static final int PERMISSION_CODE_MICROPHONE = PERMISSION_CODE_LOCATION << 1;
    public static final int PERMISSION_CODE_READ_PHONE_STATE = PERMISSION_CODE_MICROPHONE << 1;
    public static final int PERMISSION_CODE_CALL_PHONE = PERMISSION_CODE_READ_PHONE_STATE << 1;
    public static final int PERMISSION_CODE_READ_CALL_LOG = PERMISSION_CODE_CALL_PHONE << 1;
    public static final int PERMISSION_CODE_SENSORS = PERMISSION_CODE_READ_CALL_LOG << 1;
    public static final int PERMISSION_CODE_SMS = PERMISSION_CODE_SENSORS << 1;
    public static final int PERMISSION_CODE_STORAGE = PERMISSION_CODE_SMS << 1;
    /** 有25个危险权限,我们定义24个,忽略一个,留个小坑 */
    public static final int PERMISSION_CODE_ALL = (1<<24) - 1; // 24个1

    private static final Map<String, Integer> permission2CodeMap = new HashMap<>();
    private static final Map<String, String> permission2zhMap = new HashMap<>();

    static {
        permission2CodeMap.put(Manifest.permission.READ_CALENDAR, PERMISSION_CODE_CALENDAR);
        permission2CodeMap.put(Manifest.permission.READ_PHONE_STATE, PERMISSION_CODE_READ_PHONE_STATE);
        permission2CodeMap.put(Manifest.permission.READ_CALL_LOG, PERMISSION_CODE_CALL_PHONE);
        permission2CodeMap.put(Manifest.permission.READ_EXTERNAL_STORAGE, PERMISSION_CODE_STORAGE);
        permission2CodeMap.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PERMISSION_CODE_STORAGE);

        permission2zhMap.put(Manifest.permission.READ_CALENDAR, App.getAppContext().getString(R.string.permission_read_calendar));
        permission2zhMap.put(Manifest.permission.READ_PHONE_STATE, App.getAppContext().getString(R.string.permission_read_phone_state));
        permission2zhMap.put(Manifest.permission.READ_CALL_LOG, App.getAppContext().getString(R.string.permission_read_call_log));
        permission2zhMap.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, App.getAppContext().getString(R.string.permission_write_sdcard));
    }

    protected static int getPermissionCode(String permission) {
        return permission2CodeMap.get(permission);
    }

    protected static String getPermissionZh(String permission) {
        return permission2zhMap.get(permission);
    }

    protected static @NonNull String[] getPermissionsZh(String... permissions) {
        int size = permissions.length;
        if (size <= 0) {
            return null;
        }
        String[] permissionsZh = new String[size];
        for (int i = 0; i < size; i++) {
            permissionsZh[i] = permission2zhMap.get(i);
        }
        return permissionsZh;
    }

    /**
     * 检查是否有权限,如果没有,则申请。没有考虑场景
     * @param activity
     * @param permission
     * @return
     */
    protected static boolean checkRequestSinglePermission(Activity activity, String permission) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            int permissionState = activity.checkSelfPermission(permission);
            if (permissionState == PackageManager.PERMISSION_GRANTED) {
                return true;
            } else {
                activity.requestPermissions(new String[]{permission}, permission2CodeMap.get(permission));
                return false;
            }
        } else {
            return true;
        }
    }

    protected static boolean checkRequestMultiPermissions(Activity activity, String... permissions) {
        return checkRequestMultiPermissions(activity, 0, permissions);
    }

    /**
     * @param scene     场景
     */
    protected static boolean checkRequestMultiPermissions(Activity activity, int scene, String... permissions) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            ArrayList<String> deniedPermissions = new ArrayList<>();
            for(String permission: permissions) {
                int permissionState = activity.checkSelfPermission(permission);
                if (permissionState == PackageManager.PERMISSION_DENIED) {
                    deniedPermissions.add(permission);
                }
            }
            int deniedPermissionCount = deniedPermissions.size();
            if (deniedPermissionCount == 0) {
                return true;
            } else {
                String[] deniedPermissionArray =  deniedPermissions.toArray(new String[0]);
                int deniedPermissionCode = 0;
                for (String permission: deniedPermissionArray) {
                    deniedPermissionCode |= permission2CodeMap.get(permission).intValue();
                }
                activity.requestPermissions(deniedPermissionArray, deniedPermissionCode | scene/*拼接场景和权限码*/);
                return false;
            }
        } else {
            return true;
        }
    }

    public static final int SCENE_FLAG = 0b1000000000000000000000000; // 2^24,requestCode大于这个值,说明带场景了
    public static final int SCENE_MAX = 0b11111111000000000000000000000000;

    public static boolean isDefaultScene(int requestCode) {
        return requestCode < SCENE_FLAG;
    }

    public static int getScene(int requestCode) {
        return requestCode & SCENE_MAX;
    }

    public static int getPermissionCodeIgnoreScene(int requestCode) {
        return requestCode & PERMISSION_CODE_ALL;
    }
}
View Code

接收处的处理,从requestCode解析出场景和权限

    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (PermissionUtils.isDefaultScene(requestCode)) {
            /* 默认场景 */
            ArrayList<String> deniedPermissions = new ArrayList<>();
            for (int i = 0, size = permissions.length; i < size; i++) {
                if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                    deniedPermissions.add(permissions[i]);
                }
            }
            if (deniedPermissions.size() == 0) {
                startActivity(new Intent(this, MainActivity.class));
            } else {
                showPermissionDialog(deniedPermissions);
            }
            return;
        } else {
            /* 自定义的特殊场景,解析出场景和权限 */
            int permissionCode = PermissionUtils.getPermissionCodeIgnoreScene(requestCode);
            int scene = PermissionUtils.getScene(requestCode);
        }
    }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM