美文网首页Android收藏集Android学习Android开发经验谈
Android 动态权限设计 (权限的申请与处理)

Android 动态权限设计 (权限的申请与处理)

作者: Android技术分享 | 来源:发表于2018-02-23 13:53 被阅读382次

    Demo下载地址:https://pan.baidu.com/s/1dnaugm


    需求:

    最近把APP的TargetSdk从21提高至25,梳理了一下,APP中有多个地方用到了动态权限。
    所以,需要把动态权限的申请与处理统一设计。

    动态权限的基础知识,不再累述,请自行查询资料。
    安卓端动态权限请求与处理的过程,通常如下:

    1. 检测权限
      ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED
    2. 在Activity中请求权限
      ActivityCompat.requestPermissions(activity, permissions, resultCode);
    3. APP弹出系统的授权提示框,让用户授权(用户可授权、也可取消、也可选择不再提醒)
    4. 在Activity的onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)方法中,返回用户授权的信息(请求码,请求的权限集合、每个权限授权的情况集合)
      a) 如果已都已授权,可以继续操作,也可以是什么也不做,等待用户重新操作;
      b) 如果未授权,引导用户去授权;

    常见问题分析 与 真机实验:

    总结安卓动态权限时,在网上也看了不少资料,反应最多的问题就是:
    无法绝对检测权限的状态(授予、拒绝、禁止(不再提醒))!!!
    原因如:各大手机厂商的定制或自身的权限体系与谷歌不一致、checkSelfPermission无效、shouldShowRequestPermissionRationale无效等等,对应的解释也是千奇百怪。
    与其那么纠结,不如自己动手实验一下,看一看动态权限的判断是否如传说中的那么艰难。

    1. 实验目的:测试检测权限状态的方法
    2. 实验机器:小米7.1.2 三星6.0.1
    3. 查询官方资料,查找对应的API
    用户权限状态检测的 主要方法有3种,谷歌官方大概释义如下:
    //方法1:检测是否授予某权限,如果是返回0,反之,返回-1
    ContextCompat.checkSelfPermission(activity, permission);
    //方法2:是否应该向用户显示请求权限的原因说明(true/false)
    ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
    //方法3:判断程序的某操作是被允许的还是拒绝的还是忽略的
    AppOpsManagerCompat.noteProxyOp(activity, op, activity.getPackageName());
    
    1. Google将权限进行了分组,申请属于normal permissions的权限在manifest中声明即可,对于dangerous permissions 权限必须运行时动态申请。
      normal permissions权限如下:
    android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
    android.permission.ACCESS_NETWORK_STATE
    android.permission.ACCESS_NOTIFICATION_POLICY
    android.permission.ACCESS_WIFI_STATE
    android.permission.ACCESS_WIMAX_STATE
    android.permission.BLUETOOTH
    android.permission.BLUETOOTH_ADMIN
    android.permission.BROADCAST_STICKY
    android.permission.CHANGE_NETWORK_STATE
    android.permission.CHANGE_WIFI_MULTICAST_STATE
    android.permission.CHANGE_WIFI_STATE
    android.permission.CHANGE_WIMAX_STATE
    android.permission.DISABLE_KEYGUARD
    android.permission.EXPAND_STATUS_BAR
    android.permission.FLASHLIGHT
    android.permission.GET_ACCOUNTS
    android.permission.GET_PACKAGE_SIZE
    android.permission.INTERNET
    android.permission.KILL_BACKGROUND_PROCESSES
    android.permission.MODIFY_AUDIO_SETTINGS
    android.permission.NFC
    android.permission.READ_SYNC_SETTINGS
    android.permission.READ_SYNC_STATS
    android.permission.RECEIVE_BOOT_COMPLETED
    android.permission.REORDER_TASKS
    android.permission.REQUEST_INSTALL_PACKAGES
    android.permission.SET_TIME_ZONE
    android.permission.SET_WALLPAPER
    android.permission.SET_WALLPAPER_HINTS
    android.permission.SUBSCRIBED_FEEDS_READ
    android.permission.TRANSMIT_IR
    android.permission.USE_FINGERPRINT
    android.permission.VIBRATE
    android.permission.WAKE_LOCK
    android.permission.WRITE_SYNC_SETTINGS
    com.android.alarm.permission.SET_ALARM
    com.android.launcher.permission.INSTALL_SHORTCUT
    com.android.launcher.permission.UNINSTALL_SHORTCUT
    

    我们先来看看将normal 和 dangerous的权限 同时在6.0+手机上的效果吧:
    我们准备测试的权限如下,共6个:
    前面的4个为危险权限,分为3类(SD卡读写、相机、短信);
    后面的2个为标准权限,分为2类(音频调节、桌面快捷方式);

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.CAMERA"/>
        <uses-permission android:name="android.permission.READ_SMS"/>
        <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
        <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
    
    三星6.0.1.jpg 小米7.1.2.jpg

    通过上面两图可以看到,申请同样的权限,在不同手机上显示的效果是不同的:
    三星只显示了危险的权限,但是位置权限我们没有申请,它也给默认显示了出来;
    小米将危险权限和标准权限都显示了出来,不过标准权限默认都显示着禁用的图标,清单中倒是没有位置权限,却多了后台弹出等权限项。
    推测:不同厂商对于授权页面都有自己的定制规则。

    1. 测试方法1:检测是否授予某权限,如果是,返回0,反之,返回-1
      ContextCompat.checkSelfPermission(activity, permission);
    //动态请求的权限数组
    String[] permissions =new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.CAMERA,
                    Manifest.permission.READ_SMS,
                    Manifest.permission.INSTALL_SHORTCUT,
                    Manifest.permission.MODIFY_AUDIO_SETTINGS};
    //测试
    Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
            for (String permission : permissions) {           
                int isGranted = ContextCompat.checkSelfPermission(activity, permission);
                if (isGranted == PackageManager.PERMISSION_GRANTED) {
                    //已授权
                    Log.i("checkSelfPermission", PermissionUtils.getInstance().getPermissionName(permission)+ ":   已授权");
                }else if(isGranted == PackageManager.PERMISSION_DENIED){
                    //未授权的
                    Log.i("checkSelfPermission", PermissionUtils.getInstance().getPermissionName(permission)+ ":   未授权");
                }
            }
            Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
    

    运行两个手机,输出的结果如下:

    三星:
    02-26 15:52:07.376 3247-3247/iwangzhe.paizhaocaiqie I/checkSelfPermission: --------------------------------5
    02-26 15:52:07.396 3247-3247/iwangzhe.paizhaocaiqie I/checkSelfPermission: --文件存储:   未授权
    02-26 15:52:07.396 3247-3247/iwangzhe.paizhaocaiqie I/checkSelfPermission: --相机/拍照:   未授权
    02-26 15:52:07.396 3247-3247/iwangzhe.paizhaocaiqie I/checkSelfPermission: --短信:   未授权
    02-26 15:52:07.396 3247-3247/iwangzhe.paizhaocaiqie I/checkSelfPermission: com.android.launcher.permission.INSTALL_SHORTCUT:   已授权
    02-26 15:52:07.396 3247-3247/iwangzhe.paizhaocaiqie I/checkSelfPermission: android.permission.MODIFY_AUDIO_SETTINGS:   已授权
    02-26 15:52:07.396 3247-3247/iwangzhe.paizhaocaiqie I/checkSelfPermission: --------------------------------5
    
    小米:
    02-26 15:58:09.128 7306-7306/iwangzhe.paizhaocaiqie I/checkSelfPermission: --------------------------------5
    02-26 15:58:09.131 7306-7306/iwangzhe.paizhaocaiqie I/checkSelfPermission: --文件存储:   未授权
    02-26 15:58:09.131 7306-7306/iwangzhe.paizhaocaiqie I/checkSelfPermission: --相机/拍照:   未授权
    02-26 15:58:09.132 7306-7306/iwangzhe.paizhaocaiqie I/checkSelfPermission: --短信:   未授权
    02-26 15:58:09.132 7306-7306/iwangzhe.paizhaocaiqie I/checkSelfPermission: com.android.launcher.permission.INSTALL_SHORTCUT:   已授权
    02-26 15:58:09.132 7306-7306/iwangzhe.paizhaocaiqie I/checkSelfPermission: android.permission.MODIFY_AUDIO_SETTINGS:   已授权
    02-26 15:58:09.132 7306-7306/iwangzhe.paizhaocaiqie I/checkSelfPermission: --------------------------------5
    

    通过上面两图可以看出,两种手机输出的结果都是一样的,也都是正确的。
    只是小米的手机显示的状态与输出的结果有些歧义,对于标准权限(音频调节、快捷方式),明明标识着拒绝的图标(红×),输出的结果却是已授权(已授权是正确的结果)。这可能是小米手机本身的小问题吧,这也可能也是迷惑很多开发者的主要原因。

    1. 测试方法2:是否应该向用户显示请求权限的原因说明(true/false)
      ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
    String[] permissions =new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.CAMERA,
                    Manifest.permission.READ_SMS,
                    Manifest.permission.INSTALL_SHORTCUT,
                    Manifest.permission.MODIFY_AUDIO_SETTINGS};
    //测试
    Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
            for (String permission : permissions) {
                //拒绝且不再提醒、系统默认禁用的权限、用户未点过拒绝的权限均返回false, 只有用户点击拒绝后,且没有勾选不再提醒返回true
                if(!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)){
                    Log.i("checkSelfPermission @@", PermissionUtils.getInstance().getPermissionName(permission)+ ":   false");
                }else{
                    Log.i("checkSelfPermission @@", PermissionUtils.getInstance().getPermissionName(permission)+ ":   true");
                }
            }
            Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
    

    运行两个手机,直接运行,默认输出的结果均一致

    小米、三星:
    02-26 16:02:59.382 13325-13325/iwangzhe.paizhaocaiqie I/checkSelfPermission: --------------------------------5
    02-26 16:02:59.383 13325-13325/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: --文件存储:   false
    02-26 16:02:59.384 13325-13325/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: --相机/拍照:   false
    02-26 16:02:59.384 13325-13325/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: --短信:   false
    02-26 16:02:59.384 13325-13325/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: com.android.launcher.permission.INSTALL_SHORTCUT:   false
    02-26 16:02:59.385 13325-13325/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: android.permission.MODIFY_AUDIO_SETTINGS:   false
    02-26 16:02:59.385 13325-13325/iwangzhe.paizhaocaiqie I/checkSelfPermission: --------------------------------5
    

    允许SD卡存储、拒绝拍照(未勾选不再提醒)后的输出结果,如下:

    小米、三星:
    02-26 16:07:09.383 16248-16248/iwangzhe.paizhaocaiqie I/checkSelfPermission: --------------------------------5
    02-26 16:07:09.385 16248-16248/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: --文件存储:   false
    02-26 16:07:09.386 16248-16248/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: --相机/拍照:   true
    02-26 16:07:09.387 16248-16248/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: --短信:   false
    02-26 16:07:09.388 16248-16248/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: com.android.launcher.permission.INSTALL_SHORTCUT:   false
    02-26 16:07:09.390 16248-16248/iwangzhe.paizhaocaiqie I/checkSelfPermission @@: android.permission.MODIFY_AUDIO_SETTINGS:   false
    02-26 16:07:09.390 16248-16248/iwangzhe.paizhaocaiqie I/checkSelfPermission: --------------------------------5
    
    小米手机测试.png

    经过反复测试:
    当用户禁用(拒绝且不再提醒)、系统默认禁用的权限、用户未点过拒绝的权限、用户授权的权限均返回false;
    只有用户点击拒绝后,且没有勾选不再提醒的权限返回true;

    7.测试方法3:判断程序的某操作是被允许的还是拒绝的还是忽略的
    AppOpsManagerCompat.noteProxyOp(activity, op, activity.getPackageName());

    String[] permissions =new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.CAMERA,
                    Manifest.permission.READ_SMS,
                    Manifest.permission.INSTALL_SHORTCUT,
                    Manifest.permission.MODIFY_AUDIO_SETTINGS};
    //测试
    Log.i("checkSelfPermission", "***********************"+permissions.length);
            for (String permission : permissions) {
                String op = AppOpsManagerCompat.permissionToOp(permission);
                //判断非空
                if (TextUtils.isEmpty(op)) continue;
                int result = AppOpsManagerCompat.noteProxyOp(activity, op, activity.getPackageName());
                if (result == AppOpsManagerCompat.MODE_IGNORED) {
                    Log.i("checkSelfPermission", "**" + permission + "  false");
                } else if(result == AppOpsManagerCompat.MODE_ALLOWED) {
                    Log.i("checkSelfPermission", "**" + permission + "  true");
                }else if(result == AppOpsManagerCompat.MODE_DEFAULT) {
                    Log.i("checkSelfPermission", "**" + permission + "  default");
                }
            }
            Log.i("checkSelfPermission", "***********************"+permissions.length);
    
    02-26 16:18:03.016 6714-6714/iwangzhe.paizhaocaiqie I/checkSelfPermission: ***********************5
    02-26 16:18:03.017 6714-6714/iwangzhe.paizhaocaiqie I/checkSelfPermission: **android.permission.READ_EXTERNAL_STORAGE  true
    02-26 16:18:05.546 6714-6714/iwangzhe.paizhaocaiqie I/checkSelfPermission: **android.permission.CAMERA  false
    02-26 16:18:05.546 6714-6714/iwangzhe.paizhaocaiqie I/checkSelfPermission: **android.permission.READ_SMS  false
    02-26 16:18:05.547 6714-6714/iwangzhe.paizhaocaiqie I/checkSelfPermission: ***********************5
    
    
    4.png

    由上面两图看到,我们请求了5个权限,但是只输出的3项权限,均为危险权限,而标准权限没有任何输出,因为转op时为null了,可能是标准权限不在转换清单中,所以....。
    经过测试,获得的危险权限授权状态的结果与checkSelfPermission方法返回的结果一致。

    1. 关于低版本适配
      动态权限是在6.0以上才提供的,所以我们不需要想的太复杂,只需要在23以上判断请求动态权限,23以下按照以往的方式处理即可。

    设计思路:

    1. 动态权限为独立的体系,可以设计成单独的模块;
    2. 在Activity中动态权限的处理尽量简洁,应封装权限的检查与请求、权限结果回调等操作;
    3. 应考虑权限请求、处理的可扩展性
    4. 权限的检测和请求可统一封装为 “权限请求”,权限结果回调可统一封装为“权限结果处理”


      权限请求.png

      通过上图,可以看到,权限判断被封装进了权限请求中,所以我们在请求/判断权限时,统一调用接口IRequestPermissions即可;

    权限结果处理.png

    通过上图,可以看到,权限结果处理方案有多种,我们此处采取的是策略模式,以扩展更多的方案;
    RequestPermissionsResult、RequestPermissionsResultSetApp分别表示不同的处理方案。
    那么,我们再处理权限结果回调时,仅需调用接口IRequestPermissionsResult即可。


    先来看下代码中是如何调用的:
    第一步:实例化 “权限请求”,“权限结果处理”

    IRequestPermissions requestPermissions = RequestPermissions.getInstance();//动态权限请求
    IRequestPermissionsResult requestPermissionsResult = RequestPermissionsResultSetApp.getInstance();//动态权限请求结果处理
    

    第二步:在需要的地方,请求权限

    //请求权限
        private boolean requestPermissions(){
            //需要请求的权限
            String[] permissions = {Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.CAMERA};
            //开始请求权限
            return requestPermissions.requestPermissions(
                    this,
                    permissions,
                    PermissionUtils.ResultCode1);
        }
    

    第三步:onRequestPermissionsResult回调中,统一处理结果

    //用户授权操作结果(可能授权了,也可能未授权)
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            //用户给APP授权的结果
            //判断grantResults是否已全部授权,如果是,执行相应操作,如果否,提醒开启权限
            if(requestPermissionsResult.doRequestPermissionsResult(this, permissions, grantResults)){
                //请求的权限全部授权成功,此处可以做自己想做的事了
                //输出授权结果
                Toast.makeText(MainActivity.this,"授权成功,请重新点击刚才的操作!",Toast.LENGTH_LONG).show();
            }else{
                //输出授权结果
                Toast.makeText(MainActivity.this,"请给APP授权,否则功能无法正常使用!",Toast.LENGTH_LONG).show();
            }
        }
    

    通过这3步,即可快速实现动态权限的所有操作。
    而如何检测、判断、请求的权限,如何引导用户设置权限等相关事项,开发人员不需要再关注。


    具体的实现:

    1. 权限请求


      3.png

      权限请求接口

    public interface IRequestPermissions {
        /**
         * 请求权限
         * @param activity 上下文
         * @param permissions 权限集合
         * @param resultCode 请求码
         * @return 如果权限已全部允许,返回true; 反之,请求权限,在
         */
        boolean requestPermissions(Activity activity, String[] permissions, int resultCode);
    }
    

    权限请求实现类

    public class RequestPermissions implements IRequestPermissions {
        private static RequestPermissions requestPermissions;
        public static RequestPermissions getInstance(){
            if(requestPermissions == null){
                requestPermissions = new RequestPermissions();
            }
            return requestPermissions;
        }
    
        @Override
        public boolean requestPermissions(Activity activity, String[] permissions, int resultCode) {
            //判断手机版本是否23以下,如果是,不需要使用动态权限
            if(Build.VERSION.SDK_INT < 23){
                return true;
            }
    
            //判断并请求权限
            return requestNeedPermission(activity,permissions,resultCode);
        }
    
        private boolean requestAllPermission(Activity activity, String[] permissions, int resultCode){
            //判断是否已赋予了全部权限
            boolean isAllGranted = CheckPermission.checkPermissionAllGranted(activity, permissions);
            if(isAllGranted){
                return true;
            }
            ActivityCompat.requestPermissions(activity, permissions, resultCode);
            return false;
        }
    
        private boolean requestNeedPermission(Activity activity, String[] permissions, int resultCode){
            List<String> list = CheckPermission.checkPermissionDenied(activity, permissions);
            if(list.size() == 0){
                return true;
            }
    
            //请求权限
            String[] deniedPermissions = list.toArray(new String[list.size()]);
            ActivityCompat.requestPermissions(activity, deniedPermissions, resultCode);
            return false;
        }
    }
    

    权限检测类

    public class CheckPermission {
        /**
         * 检查是否拥有指定的所有权限
         * @param context 上下文
         * @param permissions 权限数组
         * @return 只要有一个权限没有被授予, 则直接返回 false,否则,返回true!
         */
        public static boolean checkPermissionAllGranted(Context context, String[] permissions) {
            for (String permission : permissions) {
                if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
            return true;
        }
    
        /**
         * 检查未允许的权限集合
         * @param context 上下文
         * @param permissions 权限集合
         * @return 未允许的权限集合
         */
        public static List<String> checkPermissionDenied(Context context, String[] permissions){
            List<String> lstPermissions = new ArrayList<>();
            for (String permission : permissions) {
                if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                    lstPermissions.add(permission);
                }
            }
            return lstPermissions;
        }
    }
    
    1. 权限请求结果处理


      4.png

    接口

    public interface IRequestPermissionsResult {
        /**
         * 处理权限请求结果
         * @param activity
         * @param permissions 请求的权限数组
         * @param grantResults 权限请求结果数组
         * @return 处理权限结果如果全部通过,返回true;否则,引导用户去授权页面
         */
        boolean doRequestPermissionsResult(Activity activity, @NonNull String[] permissions, @NonNull int[] grantResults);
    }
    

    权限请求结果实现类(方案一:如果授权失败,不做任何处理)

    public class RequestPermissionsResult implements IRequestPermissionsResult {
        private static RequestPermissionsResult requestPermissionsResult;
        public static RequestPermissionsResult getInstance(){
            if(requestPermissionsResult == null){
                requestPermissionsResult = new RequestPermissionsResult();
            }
            return requestPermissionsResult;
        }
        @Override
        public boolean doRequestPermissionsResult(Activity activity, @NonNull String[] permissions, @NonNull int[] grantResults) {
            boolean isAllGranted = true;
            // 判断是否所有的权限都已经授予了
            for (int grant : grantResults) {
                if (grant != PackageManager.PERMISSION_GRANTED) {
                    isAllGranted = false;
                    break;
                }
            }
            //已全部授权
            if (isAllGranted) {
                return true;
            }
            else {
                //什么也不做
            }
            return false;
        }
    }
    

    权限请求结果实现类(方案二:如果授权失败,引导用户进行应用授权)

    public class RequestPermissionsResultSetApp implements IRequestPermissionsResult{
        private static RequestPermissionsResultSetApp requestPermissionsResult;
        public static RequestPermissionsResultSetApp getInstance(){
            if(requestPermissionsResult == null){
                requestPermissionsResult = new RequestPermissionsResultSetApp();
            }
            return requestPermissionsResult;
        }
    
        @Override
        public boolean doRequestPermissionsResult(Activity activity, @NonNull String[] permissions, @NonNull int[] grantResults) {
            List<String> deniedPermission = new ArrayList<>();
            for (int i=0; i<grantResults.length;i++){
                if(grantResults[i] == PackageManager.PERMISSION_DENIED){
                    deniedPermission.add(permissions[i]);
                }
            }
    
            //已全部授权
            if (deniedPermission.size() == 0) {
                return true;
            }
            //引导用户去授权
            else {
                String name = PermissionUtils.getInstance().getPermissionNames(deniedPermission);
                SetPermissions.openAppDetails(activity,name);
            }
            return false;
        }
    }
    

    引导用户去授权

    public class SetPermissions {
        /**
         * 打开APP详情页面,引导用户去设置权限
         * @param activity 页面对象
         * @param permissionNames 权限名称(如是多个,使用\n分割)
         */
        public static void openAppDetails(final Activity activity, String permissionNames) {
            StringBuilder sb = new StringBuilder();
            sb.append(PermissionUtils.PermissionTip1);
            sb.append(permissionNames);
            sb.append(PermissionUtils.PermissionTip2);
            AlertDialog.Builder builder = new AlertDialog.Builder(activity);
            builder.setMessage(sb.toString());
            builder.setPositiveButton(PermissionUtils.PermissionDialogPositiveButton, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent();
                    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                    intent.addCategory(Intent.CATEGORY_DEFAULT);
                    intent.setData(Uri.parse("package:" + activity.getPackageName()));
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
                    intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
                    activity.startActivity(intent);
                }
            });
            builder.setNegativeButton(PermissionUtils.PermissionDialogNegativeButton, null);
            builder.show();
        }
    }
    
    1. 公共内容提取(常量、方法等)
    public class PermissionUtils {
        public static int ResultCode1 = 100;//权限请求码
        public static int ResultCode2 = 200;//权限请求码
        public static int ResultCode3 = 300;//权限请求码
        public static String PermissionTip1 = "亲爱的用户 \n\n软件部分功能需要请求您的手机权限,请允许以下权限:\n\n";//权限提醒
        public static String PermissionTip2 = "\n请到 “应用信息 -> 权限” 中授予!";//权限提醒
        public static String PermissionDialogPositiveButton = "去手动授权";
        public static String PermissionDialogNegativeButton = "取消";
    
        private static PermissionUtils permissionUtils;
        public static PermissionUtils getInstance(){
            if(permissionUtils == null){
                permissionUtils = new PermissionUtils();
            }
            return permissionUtils;
        }
    
        private HashMap<String,String> permissions;
        public HashMap<String,String> getPermissions(){
            if(permissions == null){
                permissions = new HashMap<>();
                initPermissions();
            }
            return permissions;
        }
    
        private void initPermissions(){
            //联系人/通讯录权限
            permissions.put("android.permission.WRITE_CONTACTS","--通讯录/联系人");
            permissions.put("android.permission.GET_ACCOUNTS","--通讯录/联系人");
            permissions.put("android.permission.READ_CONTACTS","--通讯录/联系人");
            //电话权限
            permissions.put("android.permission.READ_CALL_LOG","--电话");
            permissions.put("android.permission.READ_PHONE_STATE","--电话");
            permissions.put("android.permission.CALL_PHONE","--电话");
            permissions.put("android.permission.WRITE_CALL_LOG","--电话");
            permissions.put("android.permission.USE_SIP","--电话");
            permissions.put("android.permission.PROCESS_OUTGOING_CALLS","--电话");
            permissions.put("com.android.voicemail.permission.ADD_VOICEMAIL","--电话");
            //日历权限
            permissions.put("android.permission.READ_CALENDAR","--日历");
            permissions.put("android.permission.WRITE_CALENDAR","--日历");
            //相机拍照权限
            permissions.put("android.permission.CAMERA","--相机/拍照");
            //传感器权限
            permissions.put("android.permission.BODY_SENSORS","--传感器");
            //定位权限
            permissions.put("android.permission.ACCESS_FINE_LOCATION","--定位");
            permissions.put("android.permission.ACCESS_COARSE_LOCATION","--定位");
            //文件存取
            permissions.put("android.permission.READ_EXTERNAL_STORAGE","--文件存储");
            permissions.put("android.permission.WRITE_EXTERNAL_STORAGE","--文件存储");
            //音视频、录音权限
            permissions.put("android.permission.RECORD_AUDIO","--音视频/录音");
            //短信权限
            permissions.put("android.permission.READ_SMS","--短信");
            permissions.put("android.permission.RECEIVE_WAP_PUSH","--短信");
            permissions.put("android.permission.RECEIVE_MMS","--短信");
            permissions.put("android.permission.RECEIVE_SMS","--短信");
            permissions.put("android.permission.SEND_SMS","--短信");
            permissions.put("android.permission.READ_CELL_BROADCASTS","--短信");
        }
    
        /**
         * 获得权限名称集合(去重)
         * @param permission 权限数组
         * @return 权限名称
         */
        public String getPermissionNames(List<String> permission){
            if(permission==null || permission.size()==0){
                return "\n";
            }
            StringBuilder sb = new StringBuilder();
            List<String> list = new ArrayList<>();
            HashMap<String,String> permissions = getPermissions();
            for(int i=0; i<permission.size(); i++){
                String name = permissions.get(permission.get(i));
                if(name!=null && !list.contains(name)){
                    list.add(name);
                    sb.append(name);
                    sb.append("\n");
                }
            }
            return sb.toString();
        }
    }
    

    效果图:

    请求权限.jpg 请求权限--带不再提醒6.jpg 引导用户去授权--包含需要的权限名称.jpg

    Demo下载地址:https://pan.baidu.com/s/1dnaugm


    注意:
    以上内容只是为了大家能清晰的理解动态权限的使用,Demo可以作为代码参考,但是不应拿到项目中直接使用,因为不同的项目中有不同的要求和限制。使用该Demo中的代码时,请根据自己项目的要求,进一步优化、调整Demo的代码(如什么场合使用checkSelfPermission、shouldShowRequestPermissionRationale还是noteProxyOp等)

    本案例如有问题,请及时反馈给我们,感谢!

    相关文章

      网友评论

      本文标题:Android 动态权限设计 (权限的申请与处理)

      本文链接:https://www.haomeiwen.com/subject/wuatxftx.html