Android6.0运行时权限

权限分为两大类:
Normal Permissions

Dangerous Permission

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

Android6.0运行时权限_第1张图片
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 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运行时权限)