前言
阅读前说明:
- 所有系统均为Android原生系统,其他国产ROM最后再讨论。
- 所有的申请都为主动触发,即主动点击申请按钮。
- 申请时,默认是没有权限的。
- 流程图中长方形中为方法名。
流程图
申请单个权限流程图
申请权限组流程图
申请混合权限(单个权限+权限组)流程图
具体流程=单个权限流程图+权限组流程图
代码中有例子,就不详细说明了。
EasyPermissions使用
easypermissions
- 依赖
dependencies {
implementation 'pub.devrel:easypermissions:1.2.0'
}
- 检查权限并申请权限
public static final int PERMISSION_STORAGE_CODE = 10001;
public static final String PERMISSION_STORAGE_MSG = "此app需要获取SD卡读取权限";
public static final String[] PERMISSION_STORAGE = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE};
public static boolean hasPermissions(Context context, String... permissions) {
return EasyPermissions.hasPermissions(context, permissions);
}
/**
* 是否有SD卡权限
*
* @param context
* @return
*/
public static boolean hasStoragePermission(Context context) {
return hasPermissions(context, PERMISSION_STORAGE);
}
@AfterPermissionGranted(PERMISSION_STORAGE_CODE)
public void initSimple() {
if (hasStoragePermission(context)) {
//有权限
} else {
//申请权限
EasyPermissions.requestPermissions(context, PERMISSION_STORAGE_MSG, PERMISSION_STORAGE_CODE, PERMISSION_STORAGE);
}
}
- 实现
EasyPermissions.PermissionCallbacks
接口,直接处理权限是否成功申请
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Forward results to EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
//成功
@Override
public void onPermissionsGranted(int requestCode, List list) {
// Some permissions have been granted
// ...
}
//失败
@Override
public void onPermissionsDenied(int requestCode, List list) {
// Some permissions have been denied
// ...
}
Dailog说明
- RationaleDialog:申请权限的说明。
[图片上传失败...(image-da33d8-1530149961979)]
EasyPermissions.requestPermissions(context, PERMISSION_STORAGE_MSG, PERMISSION_STORAGE_CODE, PERMISSION_STORAGE);
或者
EasyPermissions.requestPermissions(
new PermissionRequest.Builder(this, RC_CAMERA_AND_LOCATION, perms)
.setRationale(R.string.camera_and_location_rationale)
.setPositiveButtonText(R.string.rationale_ask_ok)
.setNegativeButtonText(R.string.rationale_ask_cancel)
.setTheme(R.style.my_fancy_style)
.build());
方法回调实现EasyPermissions.RationaleCallbacks
接口:
/**
* dialog提醒点击“确定”
*
* @param requestCode
*/
@Override
public void onRationaleAccepted(int requestCode) {
}
/**
* dialog提醒点击“取消”
*
* @param requestCode
*/
@Override
public void onRationaleDenied(int requestCode) {
}
详细使用参考:easypermissions
- AppSettingsDialog:跳转setting页,申请被拒绝时使用。
/**
* 拒绝权限
*
* @param requestCode
* @param perms
*/
@Override
public void onPermissionsDenied(int requestCode, @NonNull List perms) {
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
new AppSettingsDialog.Builder(this)
.setTitle("提醒")
.setRationale("此app需要这些权限才能正常使用")
.build()
.show();
}
}
方法回调
/**
/**
* 设置页面回调
*
* @param requestCode
* @param resultCode
* @param data
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE) {
if (PermissionUtils.hasStoragePermission(context)){
//有权限
}else{
//没有权限
}
}
}
国产ROM适配
国产ROM问题一直是最多的,申请权限的弹窗不按Android原生来,有时候允许权限了,回调却是走失败。
如果想适配国产ROM,我推荐 AndPermission
AndPermission概述
AndPermission可以简化在Android上请求权限的流程。
- 在Android6.0或者更高版本的系统中请求运行时权限。
- 在Android7.0或者更高版本的系统中分享私有文件。
- 在Android8.0或者更高版本的系统中安装Apk文件。
请求流程
为了兼容一些特殊中国产手机,AndPermission请求权限的流程是这样的:
发起请求
│
调用SDK Api判断是否有权限
│
┌─────────────┴─────────────┐
有权限 没权限
│ │
│ 使用SDK Api请求权限
│ │
│ 用户授权或者拒绝
└─────────────┬─────────────┘
执行权限相关代码
┌───────┴───────┐
正常 异常
│ │
onGranted() onDenied()
┌──────┴──────┐ │
正常 异常───────┘
申请流程的释义
可以看出AndPermission的申请流程和标准的申请流程大相径庭,这样做主要是因为以下几点原因。
- 兼容Android5.0的系统判断是否有权限。
- 部分设备上使用SDK的Api判断是否有权限时,无论是否有权限都返回true。
- 部分设备上无论用户点击同意还是拒绝都返回true。
- 部分设备在申请权限时并不会弹出授权Dialog,而是在执行权限相关代码时才会弹出授权Dialog。
特特特别注意:
因为部分权限(例如发送短信、打电话、接听/挂断电话等),AndPermission不能执行权限相关代码做测试,所以对于这一类权限在执行权限相关代码步骤仅仅使用了AppOpsManager做检测。因为极少部分国产机总是返回true,因此直接回调到onGranted()中让开发者执行相关代码来辅助AndPermission的整个流程,如果onGranted()方法发生异常,那么则认为是没有权限,所以重新回调到onDenied()方法中,如果onGranted()方法正常执行那么则认为有权限。这样一来刚好是一个完整的流程,也可以最大限度的兼容到更多异常的手机。
回调onGranted()时AndPermission对onGranted()方法做了try catch。这样做可以保证两点,第一点是防止部分手机返回错误状态时回调了onGranted()后的崩溃,第二点是防止部分手机需要真正执行权限相关代码时触发授权Dialog后被用户拒绝后的崩溃。而这两点都是没有权限,因此会重新回调到onDenied()中让开发者处理没权限的情况。
综上所述,如果onGranted()和onDenied()方法都被调用了,说明开发者的onGranted()方法发生了异常。
AndPermission详细说明文档
GitHub
GitHub