Android6.0以后的权限管理发生了很大的改变,不是直接在manifest中添加,而是让用户使用到的时候去动态的申请,Google把权限分了类,当涉及到一些危险的权限的时候必须使用动态申请。

  • 权限分类
  • 重要方法
  • 使用示例
  • 权限申请封装
  • 其他权限

权限分类

如下是危险权限,使用的时候都是需要动态申请的

Dangerous Permissions:
// 涉及读写联系人,访问账户
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
// 涉及SMS卡的操作
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)ContextCompat.checkSelfPermission
(2)ActivityCompat.requestPermissions
(3)ActivityCompat.shouldShowRequestPermissionRationale
(4)onRequestPermissionsResult

1. ContextCompat.checkSelfPermission

检查是否具有某个权限,如果应用具有此权限,方法将返回PackageManager.PERMISSION_GRANTED,并且应用可以继续操作。如果应用不具有此权限,方法将返回PackageManager.PERMISSION_DENIED,且应用必须明确向用户要求权限。

2. ActivityCompat.requestPermissions

应用可以通过这个方法动态申请权限,调用后会弹出一个对话框提示用户授权所申请的权限。

3. ActivityCompat.shouldShowRequestPermissionRationale

如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false。

4. onRequestPermissionsResult

当应用请求权限时,系统将向用户显示一个对话框。当用户响应时,系统将调用应用的 onRequestPermissionsResult() 方法,这个是执行requestPermissions后的回调。

使用示例

1、ContextCompat.checkSelfPermission使用

private void checkPermission() {
    //动态请求的权限数组
    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(this, permission);
        if (isGranted == PackageManager.PERMISSION_GRANTED) {
            //已授权
            Log.i("checkSelfPermission", permission.toString()+ ":   已授权");
        }else if(isGranted == PackageManager.PERMISSION_DENIED){
            //未授权的
            Log.i("checkSelfPermission", permission.toString()+ ":   未授权");
        }
    }
    Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
}

2、ActivityCompat.requestPermissions使用,这里是先检查是否获得了这两个权限,只要有一个没有获得就去申请权限

private void requestPermissions() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 1);
        }else {
            Toast.makeText(this,"您已经申请了权限!",Toast.LENGTH_SHORT).show();
        }
    }
}

3、ActivityCompat.shouldShowRequestPermissionRationale使用,拒绝且不再提醒、系统默认禁用的权限、用户未点过拒绝的权限均返回false, 只有用户点击拒绝后,且没有勾选不再提醒返回true

private void rationablePermission() {
    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(this, permission)){
            Log.i("checkSelfPermission @@", permission.toString()+ ":   false");
        }else{
            Log.i("checkSelfPermission @@", permission.toString()+ ":   true");
        }
    }
    Log.i("checkSelfPermission", "--------------------------------"+permissions.length);
}

4、onRequestPermissionsResult使用,当应用请求权限时候,系统将向用户显示一个对话框。当用户相应的时候,系统将调用onRequstPermissionsResult() 方法。向其传递用户的相应。我们需要在我们自己Activity中复写该方法。并且对用户操作的反馈做处理。

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission was granted, yay! Do the
            } else {
                // permission denied, boo! Disable the
            }
            return;
        }
        // other 'case' lines to check for other
    }
}

权限申请封装

首先建立一个权限申请成功和失败的回调接口:

public interface Callback {
    //pendingPermissions需要申请的权限列表。
    void onPendingGrant(Activity activity, List<String> pendingPermissions);

    void onGrantAll();
}

然后建立一个PermissinoHelper类来检查是否有权限,如果没有的话直接申请:

/**
     * 检查权限(仅仅做检查操作,不会主动申请权限)
     *
     * @param activity    当前活动
     * @param callback    回调:1.onGrantAll 所检查权限已全部授权 2. onPendingGrant 待授权的权限集合
     * @param permissions 待检测的权限
     */
    public static void checkPermissions(Activity activity, Callback callback, String... permissions) {
        checkPermissions(activity, callback, Arrays.asList(permissions));
    }

    public static void checkPermissions(Activity activity, Callback callback, List<String> permissions) {
        if (activity == null || permissions == null || permissions.size() == 0) {
            Log.w(TAG, "Illegal param , Activity or Permissions is NULL");
            return;
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            Log.w(TAG, "The SDK API < Marshmallow");
            return;
        }
        List<String> targetPermissions = permissions;
        //检测 8.0 权限
        if (targetPermissions.contains(Permission.REQUEST_INSTALL_PACKAGES) ||
                targetPermissions.contains(Permission.ANSWER_PHONE_CALLS) ||
                targetPermissions.contains(Permission.READ_PHONE_NUMBERS)) {
            if (activity.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O) {
                Log.d(TAG, "Contain Permission over API 26");
            }
        }
        //检测 6.0 运行时权限
        List<String> noGrantedPermissions = checkPermissionState(activity, targetPermissions);
        if (noGrantedPermissions == null) {
            if (callback != null) {
                callback.onGrantAll();
            }
        } else {
            if (callback != null) {
                callback.onPendingGrant(activity, noGrantedPermissions);
            }
        }
    }
    /**
     * 获取Manifest声明的权限的集合
     *
     * @return
     */
    private static List<String> getManifestPermissions(Activity activity) {
        List<String> permissions = null;
        try {
            permissions = Arrays.asList(activity.getPackageManager().getPackageInfo(
                    activity.getPackageName(), PackageManager.GET_PERMISSIONS).requestedPermissions);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return permissions;
    }

    /**
     * 检测所申请权限的状态,并返回未授权的权限集合
     *
     * @param activity    当前活动
     * @param permissions 需要申请的目标权限的集合
     * @return 未授权的权限集合
     */
    private static List<String> checkPermissionState(Activity activity, List<String> permissions) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return null;
        }
        List<String> results = null;
        for (String permission : permissions) {
            int state = ActivityCompat.checkSelfPermission(activity, permission);
            if (state == PackageManager.PERMISSION_DENIED) {
                if (results == null) {
                    results = new ArrayList<>();
                }
                results.add(permission);
            }
        }
        return results;
    }

然后是一个申请权限的方法,他会首先判断manifest文件中是否声明了该权限

/**
     * 申请普通的6.0权限(不处理)
     *
     * @param activity           当前活动
     * @param pendingPermissions 待申请的权限集合
     */
    public static void requestPermissions(Activity activity, List<String> pendingPermissions, int requestCode) {
        if (pendingPermissions == null || pendingPermissions.size() == 0) {
            return;
        }
        List<String> manifestPermissions = getManifestPermissions(activity);
        if (manifestPermissions == null) {
            Log.w(TAG, "No permissions declared");
            return;
        } else if (manifestPermissions != null && !manifestPermissions.containsAll(pendingPermissions)) {
            Log.w(TAG, "The manifestPermissions didn't include all of pendingPermissions");
            return;
        } else {
            if (pendingPermissions.contains(Permission.SYSTEM_ALERT_WINDOW)) {
                pendingPermissions.remove(Permission.SYSTEM_ALERT_WINDOW);
                Log.w(TAG, "Call checkAndRequestAlertWindowPermission to request SYSTEM_ALERT_WINDOW permission");
            }
            if (pendingPermissions.contains(Permission.WRITE_SETTINGS)) {
                pendingPermissions.remove(Permission.WRITE_SETTINGS);
                Log.w(TAG, "Call checkAndRequestWriteSettingPermission to request WRITE_SETTINGS permission");
            }
            String[] permissions = new String[pendingPermissions.size()];
            ActivityCompat.requestPermissions(activity, pendingPermissions.toArray(permissions), requestCode);
        }
    }

如下是在Fragment或者Activity中的使用方式,用来申请所需要的权限:

//申请必须的权限。
    private void checkAndRequestPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            String[] permissions;
            if (getViewModel().getState().callType == AUDIO_TYPE) {
                permissions = new String[]{Manifest.permission.RECORD_AUDIO,
                        Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.READ_PHONE_STATE};
            } else {
                permissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO,
                        Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        Manifest.permission.READ_PHONE_STATE};
            }
            PermissionHelper.checkPermissions(this, new PermissionHelper.Callback() {
                @Override
                public void onPendingGrant(Activity activity, List<String> pendingPermissions) {
                    PermissionHelper.requestPermissions(activity, pendingPermissions, Constant.PERMISSION_REQUEST_CODE);
                }

                @Override
                public void onGrantAll() {
                    ToastUtils.showToast("All Permission has Granted");
                }
            }, permissions);
        }
    }

最后是在onRequestPermissionsResult处理权限的申请结果

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == Constant.PERMISSION_REQUEST_CODE) {
            ArrayList<String> result = new ArrayList<>();
            for (int i = 0; i < grantResults.length; i++) {
                if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                    result.add(permissions[i]);
                } else if (TextUtils.equals(permissions[i], Manifest.permission.CAMERA) && initCamera) {
                    cameraEnable();
                }
            }
		}
		ToastUtils.showToast("您尚未开启 : " + result.toString() + "权限");
		//也可以在这里引导用户去设置界面开启权限
    }

其他权限

以上这些内容全部都是来自Google的官方Demo。国内真正的Android开发环境,Rom客制化太多了,Google 原生API被改的很严重。

以小米为例:以看到小米的授权模块中,对权限操作可以分为允许,询问,拒绝。当我们第一次打开应用的时候,默认是询问状态,在该状态下,我们调用requestPermission()方法会弹出系统询问框.在弹出系统授权框后,只要你操作了(拒绝或者允许),你永远也不要想着在以后能看到授权框了,除非你过来设置这边更改为“询问”模式。不然无论你再调用几次requestPermissions(),都是直接走回调OnRequestPermissionResult。总结为一句话,小米客制的授权模块是凌驾于Google的授权模块之上的。

另外小米手机还有特殊的权限,如后台弹出权限和锁屏显示权限,如果没有打开后台弹出权限的话是没有办法从后台正常弹出的,还有锁屏也是。后台弹出权限不同手机的控制机制也是不一样的,小米是直接限制不让弹出,而vivo手机只让弹出后再给他finish掉,360手机还不一样,适配起来还是比较头大的。

再有就是通知权限,这个权限还是比较好控制的,可以直接让用户跳转到设置界面去打开。悬浮窗权限的适配也是比较麻烦的,不同手机也是不太相同。

尊重作者,尊重原创,参考文章:
封装:https://www.jianshu.com/p/8e37e9cf20a5
https://www.jianshu.com/p/2fe4fb3e8ce0