美文网首页Android第一行代码
Android6.0运行时权限

Android6.0运行时权限

作者: 傲娇的狗蛋 | 来源:发表于2016-12-12 11:34 被阅读30次

    权限分为两大类:
    Normal Permissions

    Dangerous Permission

    特点:
    对于6.0以下的权限及在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要默默忍受其一些不必要的权限(比如是个app都要访问通讯录、短信等)。而在6.0以后,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝(比如:单机的象棋对战,请求访问任何权限,我都是不同意的)。当然你也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。新的权限机制更好的保护了用户的隐私,Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。
    通俗的说:涉及到比较隐私的权限的时不光要在清单文件中声明权限,还要在代码中动态的获取权限。系统会弹出一个对话框,提示用户。

    Paste_Image.png

    两种权限的大致内容如下:
    Normal Permissions如下

    
    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 Permissions:

    
    
        group:android.permission-group.CONTACTS           //联系人组
    
        permission:android.permission.WRITE_CONTACTS      //写入联系人
    
        permission:android.permission.GET_ACCOUNTS        //访问GMail账户列表
    
        permission:android.permission.READ_CONTACTS        //读取联系人
    
    
    
        group:android.permission-group.PHONE                //手机状态组
    
        permission:android.permission.READ_CALL_LOG        //读取通话记录
    
        permission:android.permission.READ_PHONE_STATE     //手机状态 如imei等
    
        permission:android.permission.CALL_PHONE           //拨打电话
    
        permission:android.permission.WRITE_CALL_LOG       //修改通话记录
    
        permission:android.permission.USE_SIP              //允许程序使用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 //接收WAP PUSH信息
    
        permission:android.permission.RECEIVE_MMS        //允许处理、监控、接受彩信的权限
    
        permission:android.permission.RECEIVE_SMS        // 允许处理、监控、接受短信的权限
    
        permission:android.permission.SEND_SMS          //发送短信
    
        permission:android.permission.READ_CELL_BROADCASTS //获取小区广播
    

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

    授权的机制:
    如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS
    已经授权了,当你的app申请WRITE_CONTACTS
    时,系统会直接授权通过。此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个dialog是不能进行定制的)。
    不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。

    具体的操作
    1.检查是否已经开启的这个权限

     if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
                //1.先判断是否已经有权限
                if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
                    //已经有了权限
    
                } else {
                    //没有权限,去申请全选 100是申请全选的请求码
                    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 100);
                }
            }
    

    2.申请权限

    //申请权限.参数:1.上下文2.申请的权限字符串数组,请求码
    
      ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 100);
    

    3.开启了对话框。我们要知道对话框是被点击了允许还是拒绝。需要用到申请权限的回调

     /**
         * 申请全选返回,这里可以知道用户是否拒绝了申请
         */
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                               @NonNull int[] grantResults) {
            switch (requestCode) {
                case 100: //100是上面申请权限的申请码
                    if (grantResults.length > 0 && grantResults[0] == PackageManager
                            .PERMISSION_GRANTED) {
                        //用户允许权限
    
                    } else {
                        //用户拒绝权限
                        
                    }
                    break;
            }
        }
    

    对于权限的申请结果,首先验证requestCode定位到你的申请,然后验证grantResults对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果。如果申请成功,就可以做你的事情了。

    有个API值得提一下:

    // 是否应该显示解释
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
           Manifest.permission.PERMISSION_GRANTED)) {
               //用户没有点击不在询问
           }
    }else{
              //用户点击不在询问,这里无法开启弹窗,应该引导用户去设置开启全权限
      
    }
    

    例子很简单,不过需要注意的是,对于Intent这种方式,很多情况下是不需要授权的甚至权限都不需要的,比如:你是到拨号界面而不是直接拨打电话,就不需要去申请权限;打开系统图库去选择照片;调用系统相机app去牌照等。更多请参考Consider Using an Intent

    这里封装了一个工具类

    package com.example.yeqiu.test;
    
    import android.annotation.TargetApi;
    import android.app.Activity;
    import android.app.Fragment;
    
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.pm.PackageManager;
    import android.os.Build;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AlertDialog;
    
    
    import java.util.Arrays;
    import java.util.List;
    
    
    public class PermissionUtils {
    
        private static final int REQUEST_PERMISSION_CODE = 1000;
    
        private Object mContext;
    
        private PermissionListener mListener;
    
        private List<String> mPermissionList;
    
        public PermissionUtils(@NonNull Context context){
            checkCallingObjectSuitability(context);
            this.mContext = context;
    
        }
    
    
        /**
         * 权限授权申请
         * @param hintMessage
         *              要申请的权限的提示
         * @param permissions
         *              要申请的权限
         * @param listener
         *              申请成功之后的callback
         */
        public void requestPermissions(@NonNull CharSequence hintMessage,
                                       @Nullable PermissionListener listener,
                                       @NonNull final String... permissions){
    
            if(listener != null){
                mListener = listener;
            }
    
            mPermissionList = Arrays.asList(permissions);
    
            //没全部权限
            if (!hasPermissions(mContext, permissions)) {
    
                //需要向用户解释为什么申请这个权限
                boolean shouldShowRationale = false;
                for (String perm : permissions) {
                    shouldShowRationale =
                            shouldShowRationale || shouldShowRequestPermissionRationale(mContext, perm);
                }
    
                if (shouldShowRationale) {
                    showMessageOKCancel(hintMessage, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            executePermissionsRequest(mContext, permissions,
                                    REQUEST_PERMISSION_CODE);
    
                        }
                    });
                }else {
                    executePermissionsRequest(mContext, permissions,
                            REQUEST_PERMISSION_CODE);
                }
            }else if(mListener != null) { //有全部权限
                mListener.doAfterGrand(permissions);
            }
        }
    
        /**
         * 处理onRequestPermissionsResult
         * @param requestCode
         * @param permissions
         * @param grantResults
         */
        public void handleRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            switch (requestCode) {
                case REQUEST_PERMISSION_CODE:
                    boolean allGranted = true;
                    for (int grant: grantResults) {
                        if(grant != PackageManager.PERMISSION_GRANTED){
                            allGranted = false;
                            break;
                        }
                    }
    
                    if (allGranted && mListener != null) {
    
                        mListener.doAfterGrand((String[])mPermissionList.toArray());
    
                    }else if(!allGranted && mListener != null){
                        mListener.doAfterDenied((String[])mPermissionList.toArray());
                    }
                    break;
            }
        }
    
        /**
         * 判断是否具有某权限
         * @param object
         * @param perms
         * @return
         */
        public static boolean hasPermissions(@NonNull Object object, @NonNull String... perms) {
    
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                return true;
            }
    
            for (String perm : perms) {
                boolean hasPerm = (ContextCompat.checkSelfPermission(getActivity(object), perm) ==
                        PackageManager.PERMISSION_GRANTED);
                if (!hasPerm) {
                    return false;
                }
            }
    
            return true;
        }
    
    
        /**
         * 兼容fragment
         * @param object
         * @param perm
         * @return
         */
        @TargetApi(23)
        private static boolean shouldShowRequestPermissionRationale(@NonNull Object object, @NonNull String perm) {
            if (object instanceof Activity) {
                return ActivityCompat.shouldShowRequestPermissionRationale((Activity) object, perm);
            } else if (object instanceof Fragment) {
                return ((Fragment) object).shouldShowRequestPermissionRationale(perm);
            } else if (object instanceof android.app.Fragment) {
                return ((android.app.Fragment) object).shouldShowRequestPermissionRationale(perm);
            } else {
                return false;
            }
        }
    
        /**
         * 执行申请,兼容fragment
         * @param object
         * @param perms
         * @param requestCode
         */
        @TargetApi(23)
        private void executePermissionsRequest(@NonNull Object object, @NonNull String[] perms, int requestCode) {
            if (object instanceof android.app.Activity) {
                ActivityCompat.requestPermissions((Activity) object, perms, requestCode);
            } else if (object instanceof android.support.v4.app.Fragment) {
                ((android.support.v4.app.Fragment) object).requestPermissions(perms, requestCode);
            } else if (object instanceof android.app.Fragment) {
                ((android.app.Fragment) object).requestPermissions(perms, requestCode);
            }
        }
    
        /**
         * 检查传递Context是否合法
         * @param object
         */
        private void checkCallingObjectSuitability(@Nullable Object object) {
            if (object == null) {
                throw new NullPointerException("传入的context是null");
            }
    
            boolean isActivity = object instanceof android.app.Activity;
            boolean isSupportFragment = object instanceof android.support.v4.app.Fragment;
            boolean isAppFragment = object instanceof android.app.Fragment;
            if (!(isSupportFragment || isActivity || (isAppFragment && isNeedRequest()))) {
                if (isAppFragment) {
                    throw new IllegalArgumentException(
                            "Target SDK needs to be greater than 23 if caller is android.app.Fragment");
                } else {
                    throw new IllegalArgumentException("Caller must be an Activity or a Fragment.");
                }
            }
        }
    
    
        @TargetApi(11)
        private static Activity getActivity(@NonNull Object object) {
            if (object instanceof Activity) {
                return ((Activity) object);
            } else if (object instanceof android.support.v4.app.Fragment) {
                return ((android.support.v4.app.Fragment) object).getActivity();
            } else if (object instanceof android.app.Fragment) {
                return ((android.app.Fragment) object).getActivity();
            } else {
                return null;
            }
        }
    
        public static boolean isNeedRequest(){
            return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
        }
    
        public void showMessageOKCancel(CharSequence message, DialogInterface.OnClickListener okListener) {
            new AlertDialog.Builder(getActivity(mContext))
                    .setMessage(message)
                    .setPositiveButton("确定", okListener)
                    .setNegativeButton("取消", null)
                    .create()
                    .show();
        }
    
        public interface PermissionListener {
    
            void doAfterGrand(String... permission);
    
            void doAfterDenied(String... permission);
        }
    }
    
    
    
    

    使用方式:

    在activity或者fragment中需要权限的地方使用

     PermissionUtils = new PermissionUtils(MainActivity.this);
                    PermissionUtils.requestPermissions("请授予xx[相机],[读写]权限!",
                            new PermissionUtils.PermissionListener() {
                                @Override
                                public void doAfterGrand(String... permission) {
    
                                }
    
                                @Override
                                public void doAfterDenied(String... permission) {
    
                                }
                            }, Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE);
    
                }
    

    在权限回调里交给PermissionUtils处理

     @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                               @NonNull int[] grantResults) {
            PermissionUtils.handleRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    

    相关链接:http://blog.csdn.net/lmj623565791/article/details/50709663

    相关文章

      网友评论

        本文标题:Android6.0运行时权限

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