一、简介
桃夭是鸿蒙系统上的一款权限请求框架,对请求权限的代码进行高度封装,极大的简化了申请权限的代码逻辑,同时支持在Ability
、FractionAbility
、AbilitySlice
、Fractiion
里面申请权限。
二、源码
源码
建议大家把源码下载下来看看。
三、申请权限的一般步骤
- 判断是否有权限,如果有权限,直接进行下一步。
- 如果没有权限,可以弹窗告知用户申请权限的原因。
- 弹窗告知用户后,如果用户同意申请权限,则判断用户是否点击了不再提醒。
- 如果用户没有点击不再提醒,则开始申请权限。
- 如果用户点击了不再提醒,则弹窗告知用户去设置页面开启权限,用户点击弹窗后,跳转到设置页面。
- 重写
onRequestPermissionsFromUserResult
方法,判断用户是否授予权限。
每次申请权限的时候,都需要经过以上几个步骤,当申请的权限越来越多,大量的重复代码就出现了。为了减少重复代码,我封装了一个权限请求框架。
四、桃夭的使用方式
如下代码,先添加依赖,然后你只需要告知权限请求框架你需要什么权限,权限请求框架就会告知你权限申请成功还是失败。你不需要手动判断是否有权限,不需要弹窗告知用户申请权限的原因,不需要判断用户是否点击了不再提醒,不需要跳转设置页面让用户开启权限,不需要重写onRequestPermissionsFromUserResult
方法。框架把这些代码逻辑都给做了,你只需要关注权限申请成功还是失败。
申请权限变得如此之简单。
添加依赖
api 'io.gitee.zhongte:taoyao:1.0.1'
申请权限
// 申请多设备协同权限
EasyPermission.requestPermission(this, EasyPermission.DISTRIBUTED_DATASYNC, new PermissionAction() {
@Override
public void onGranted(List<String> permissions) {
// 权限申请成功
}
@Override
public void onDenied(List<String> permissions) {
// 权限申请失败
}
}, SystemPermission.DISTRIBUTED_DATASYNC);
告知用户申请权限的弹窗以及告知用户跳转到设置页面的弹窗,这两个弹窗在不同的应用里面可能长得不一样,所以这两个弹窗并没有被封装到桃夭框架里面,而是需要使用者根据你的弹窗样式对桃夭进行二次封装,我在源码里面对桃夭框架进行了二次封装,大家可以把源码下载下来,参考下我是如何对桃夭框架进行二次封装的。二次封装完成后,就可以愉快的使用上面的代码申请权限了。
五、实现原理
5、1 检测申请的权限是否在配置文件中声明
申请的权限必须在配置文件中声明,否则桃夭会直接抛异常。如何检测申请的权限是否在配置文件中声明。
如下代码,获取bundleManager
对象,通过bundleManager
对象获取应用信息,之后就可以获取应用在配置文件中声明的权限了。
/**
* 获取在配置文件中声明的权限
*
* @param context 上下文
* @return 在配置文件中声明的权限
*/
private List<String> getConfigPermissions(Context context) {
// 获取bundleManager对象
IBundleManager bundleManager = context.getBundleManager();
String bundleName = context.getBundleName();
try {
// 获取应用信息
BundleInfo bundleInfo = bundleManager.getBundleInfo(bundleName, IBundleManager.GET_BUNDLE_WITH_REQUESTED_PERMISSION);
// 获取应用在配置文件中声明的权限
List<String> reqPermissionDetails = bundleInfo.reqPermissions;
if (reqPermissionDetails == null || reqPermissionDetails.isEmpty()) {
throw new IllegalStateException("请在配置文件中声明要申请的权限");
}
return reqPermissionDetails;
} catch (RemoteException e) {
e.printStackTrace();
}
return new ArrayList<>();
}
获取到在配置文件中声明的权限后,就可以判断申请的权限是否在配置文件中了
/**
* 检查申请的权限是否在配置文件中声明
*
* @param permissions 要申请的权限
*/
private void checkPermissions(String... permissions) {
if (permissions == null || permissions.length == 0) {
throw new IllegalArgumentException("请至少申请一个权限");
}
// 获取在配置文件中声明的权限
mReqPermissions = getConfigPermissions(mOrigin.getContext());
if (mReqPermissions.isEmpty()) {
throw new IllegalStateException("请在配置文件中声明要申请的权限");
}
for (String target : permissions) {
if (!mReqPermissions.contains(target)) {
// 没有在配置中声明要申请的权限,直接抛异常
throw new IllegalStateException(String.format("%1$s权限没有配置文件中声明", target));
}
}
}
5、2 判断是否有权限
检测完权限是否在配置中声明后,就可以判断是否有权限了。这里就是通过上下文对象的verifySelfPermission
方法来判断是否有权限,如果没有权限,可以弹窗告知用户申请的原因。
/**
* 是否有权限
*
* @param context
* @param permissions
* @return
*/
@Override
public boolean hasPermission(Context context, List<String> permissions) {
for (String permission : permissions) {
int result = context.verifySelfPermission(permission);
if (result == IBundleManager.PERMISSION_DENIED) {
// 没有权限
return false;
}
}
return true;
}
5、3 判断用户是否点击了不再提醒
通过上下文对象的canRequestPermission
方法来判断用户是否点击了不再提醒。
/**
* 用户是否点击了不在提醒
*
* @param permission 权限
* @return
*/
@Override
public boolean canRequestPermission(String permission) {
return mContext.canRequestPermission(permission);
}
5、4 跳转到设置页面
如果用户点击了不再提醒,则可以跳转到设置页面让用户开启权限
/**
* 跳转到设置页面
*/
@Override
public void gotoSetting() {
try {
Intent intent = new Intent();
intent.setAction(IntentConstants.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setUri(Uri.parse("package:" + mOrigin.getContext().getBundleName()));
mOrigin.startAbility(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
5、5 启动透明的Ability
申请权限
如果没有权限,用户页面没有点击不再提醒,那就可以申请权限了。为了不让调用者重写onRequestPermissionsFromUserResult
方法,桃夭内部启动了一个Ability
。如下代码。
/**
* 开启一个透明的Ability来申请权限,这样外界就不需要重写onRequestPermissionsFromUserResult方法
*/
public class PermissionAbility extends Ability {
private static final int REQUEST_CODE = 0X10;
public static final String KEY_PERMISSION = "key_permission";
public static final String KEY_TYPE = "key_type";
public static final String SENSITIVE_PERMISSION = "sensitive_permission";
@Override
public void onStart(Intent intent) {
super.onStart(intent);
getWindow().setTransparent(true);
super.setUIContent(ResourceTable.Layout_ability_permission);
List<String> permissions = intent.getSerializableParam(KEY_PERMISSION);
String permissionType = intent.getStringParam(KEY_TYPE);
// 请求权限
requestPermissionsFromUser(permissions.toArray(new String[0]), REQUEST_CODE);
}
@Override
public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsFromUserResult(requestCode, permissions, grantResults);
// 权限的回调方法
Postman.send(permissions, grantResults);
terminateAbility();
}
@Override
protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
super.onAbilityResult(requestCode, resultCode, resultData);
}
}
直接启动一个Ability
会发生页面跳转,为了不让页面发生跳转,这里启动了一个透明的Ability
。如何将Ability
设置透明,如下代码。在abilities
节点添加metaData
,这里最关键的是Translucent
,也就是透明。
"abilities": [
{
"orientation": "unspecified",
"name": "com.poetry.taoyao.ability.PermissionAbility",
"icon": "$media:icon",
"description": "$string:permissionability_description",
"label": "$string:taoyao_PermissionAbility",
"type": "page",
"launchType": "standard",
"metaData": {
"customizeData": [
{
"name": "hwc-theme",
"value": "androidhwext:style/Theme.Emui.Translucent.NoTitleBar"
}
]
}
}
仅仅有上面的步骤好不够,需要在Ability
或者AbilitySlice
里面将窗口设置成透明。如下代码。
getWindow().setTransparent(true);
经过上面两步,也就是将Ability
的主题和窗口都设置成透明,这样就能将Ability
变成透明的了,同时也不会发生页面跳转。
5、6 判断用户是否授予权限
判断用户是否授予权限,可以使用标准的方式来判断。也就是通过grantResult
来判断用户是否授予权限。
@Override
public boolean hasPermission(int[] grantResults, String... permissions) {
if (grantResults == null || grantResults.length <= 0) {
return false;
}
for (int grantResult : grantResults) {
if (grantResult == IBundleManager.PERMISSION_DENIED) {
return false;
}
}
return true;
}
其实还有另外的方式来判断用户是否授予权限。也就是不管用户是否授权,直接访问相关业务。比如,申请录音权限,当系统回调onRequestPermissionsFromUserResult
方法时,直接去录音,如果发生异常,捕获异常说明没有权限。如下代码
/**
* 通过直接录音的方式来判断是否有录音权限
*
* @param context
* @return
* @throws Throwable
*/
@Override
public boolean test(Context context) throws Throwable {
AudioStreamInfo audioStreamInfo = new AudioStreamInfo.Builder().encodingFormat(
AudioStreamInfo.EncodingFormat.ENCODING_PCM_16BIT)
.channelMask(AudioStreamInfo.ChannelMask.CHANNEL_IN_STEREO)
.sampleRate(AUDIO_SAMPLE_RATE)
.build();
AudioCapturerInfo audioCapturerInfo = new AudioCapturerInfo.Builder().audioStreamInfo(audioStreamInfo).build();
try {
AudioCapturer capturer = new AudioCapturer(audioCapturerInfo);
// 录音
capturer.start();
new Timer().schedule(new TimerTask() {
@Override
public void run() {
capturer.stop();
}
}, AUDIO_RECORDING_TIME);
// 没有发生异常,有权限
return true;
} catch (Exception e) {
// 发生异常,无权限
return false;
}
}
桃夭在判断用户是否授权时,上面的两种方式都使用了。
至此,桃夭框架的原理基本上讲完。有兴趣的同学可以去看看源码。想要看懂源码,需要熟悉申请权限的一般步骤,桃夭其实就对这些步骤进行封装。另外还需熟悉面向接口编程、熟悉策略模式等常见设计模式。
网友评论