项目中因为SDK依赖库的关系,需要将安卓SDK升级到API 23以上。升级以后就需要适配运行时授权。Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。对于23以上的版本,Dangerous Permission应用安装以后需要运行时授权,显式授予app相关权限。
1、运行时授权原理
标准的授权过程:
但这里有个问题,那就是在系统授权弹窗环节,提醒框会有个不再提示的复选框,如果用户点击不太提示,并拒绝授权,那么再下次授权的时候,系统授权弹窗的提示框就不会在提示,所以我们很有必要需要自定义权限弹窗提示框,那么流程图就变成如下了。
对于这种情况,就需要程序处理,引导用户进行授权处理。
2、授权处理的方式
方案一:进入主页面优先处理授权,只允许用户授权成功才进行处理,防止权限不足引起一些异常。这种方式最大的优势就是避免权限不足引起一些不可预见的异常CRASH,而且避免了用户操作过程中经常出现授权的处理。
微信就是采取这种模式。
方案二:在页面运行时根据需要授权。这种模式比较灵活,给用户相对的选择权。缺点是必须要规避所有的权限不足引起的异常,而且对于一些后台的操作,例如启动后进行日志信息采集需要读取GPS、通讯录等操作,如果用户未授权的情况下,采集不到信息。而且用户不授予权限并且不在提示弹窗的情况下,基本上这些相关功能失效了。
项目中采用的“方案一+方案二”的方式。
3、授权处理
Activity授权处理分为两部分,通过requestPermission请求权限,通过onRequestPermissionsResult 处理授权结果。处理显得比较分散和麻烦。当然可以在基类中统一集成授权处理。
4、RxPermission封装了授权处理,使用很方便。实现原理:
RxPermission利用Fragment也能申请授权并且能够接收授权结果的原理,将授权处理集成在一个Fragment中。
1)RxPermission封装一个专门用于授权的Fragment类RxPermissionsFragment,用于处理申请授权和授权结果的处理;
2)将RxPermissionsFragment对象挂载到Activity上;
3)Activity调用RxPermission申请授权,通过回调接收授权结果。
5、RxPermission使用方法:
请求授权:通过RxPermission的requestEach申请多个权限。
public PermissionManager(Activity activity) {
this.activity = activity;
this.rxPermissions = new RxPermissions(activity);
rxPermissions.setLogging(true);
}
public void setRequestPermissions(String[] permissions) {
this.requestPermissions = permissions;
this.permissionCount = null != permissions ? permissions.length : 0;
this.grantedMap = new HashMap<>();
}
/**
* 请求授权
*/
public void requestPermission() {
grantedMap.clear();
rxPermissions.requestEach(requestPermissions)
.subscribe(new Consumer<Permission>() {
/**
* 授权结果回调
*
* @param permission : 每一个请求的授权权限。
* @throws Exception
*/
@Override
public void accept(Permission permission) throws Exception {
if(permission.granted) {
//全部授权成功
Log.d(TAG, "requestPermission success: " + permission);
grantedMap.put(permission.name, true);
} else if(permission.shouldShowRequestPermissionRationale) {
//显示授权弹窗
Log.d(TAG, "requestPermission failed: " + permission);
grantedMap.put(permission.name, false);
} else {
grantedMap.put(permission.name, false);
}
//授权完成时进行检测,如果仍然有权限未获取弹窗提示
if(grantedMap.size() == permissionCount) {
boolean isAllGranted = true;
//检查是否仍然存在未授权的情况
Iterator it = grantedMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Boolean> entry = (Map.Entry<String, Boolean>) it.next();
if(!entry.getValue().booleanValue()) {
//仍然存在未授权成功的权限
isAllGranted = false;
break;
}
}
if(!isAllGranted) {
//仍然有权限未授权显示授权弹窗
showPermissionDialog();
}
}
}
});
}
如果用户不再显示授权弹窗,引导用户去授权
/**
* 打开设置页面授权设置
*
*/
private void openSettingPermission() {
// vivo 点击设置图标>加速白名单>我的app
// 点击软件管理>软件管理权限>软件>我的app>信任该软件
Intent appIntent = activity.getPackageManager().getLaunchIntentForPackage("com.iqoo.secure");
if(appIntent != null){
activity.startActivityForResult(appIntent, REQUEST_CODE_SETTING);
return;
}
// oppo 点击设置图标>应用权限管理>按应用程序管理>我的app>我信任该应用
// 点击权限隐私>自启动管理>我的app
appIntent = activity.getPackageManager().getLaunchIntentForPackage("com.oppo.safe");
if(appIntent != null){
activity.startActivityForResult(appIntent, REQUEST_CODE_SETTING);
return;
}
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.fromParts("package",
activity.getApplication().getPackageName(), null));
} else {
intent.setAction(Intent.ACTION_VIEW);
intent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
intent.putExtra("com.android.settings.ApplicationPkgName",
activity.getApplication().getPackageName());
}
activity.startActivityForResult(intent, REQUEST_CODE_SETTING);
}