HarmonyOS Sample 之 Permission 原创
@toc
Permission 样例
介绍
应用权限是程序访问操作某种对象的许可。
在开发应用时会涉及系统的权限,这些权限需要用户授权才可以使用,以便系统化地规范各类应用程序的行为准则与权限许可。
本示例演示了获取位置、麦克风录音、读取日历数据、读取用户存储数据的权限申请,并通过访问相关业务确认授权是否成功。
今天来一起学习一下官方提供的Permission样例
搭建环境
安装DevEco Studio,详情请参考DevEco Studio下载。
设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
按照下面可以导入该样例。
然后找到Permission,导入项目即可。当然也可以自己去 下载 然后再打开项目。
代码结构解读
文件不多,'#'代表注释
│ config.json
│
├─java
│ └─ohos
│ └─samples
│ └─permissionapp
│ │ MainAbility.java
│ │
│ ├─slice
│ │ MainAbilitySlice.java #主能力Slice,创建按钮的监听;发起几种用户授权的请求;显示授权结果
│ │
│ └─utils
│ Constants.java #定义权限请求码
│ LogUtil.java #日志工具类
│
└─resources
└─base
├─element
│ string.json
│
├─graphic
│ bg.xml
│
├─layout
│ main_ability_slice.xml #主能力布局页面
│
└─media
icon.png
页面布局
布局就一个页面,很简单,不多讲了,重点在业务逻辑。
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2021 Huawei Device Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:width="match_parent"
ohos:height="match_parent">
<Text
ohos:width="match_parent"
ohos:height="match_content"
ohos:text="Click on button to obtain permission"
ohos:top_margin="50vp"
ohos:left_margin="10vp"
ohos:right_margin="10vp"
ohos:multiple_lines="true"
ohos:text_size="18fp"
ohos:text_color="#FF000000"
ohos:text_alignment="horizontal_center"/>
<Button
ohos:id="$+id:get_location_permission"
ohos:width="300vp"
ohos:height="40vp"
ohos:top_margin="40vp"
ohos:layout_alignment="horizontal_center"
ohos:text="Request Location permission"
ohos:text_size="20vp"
ohos:text_color="#ffffffff"
ohos:background_element="$graphic:bg"/>
<Button
ohos:id="$+id:get_microphone_permission"
ohos:width="300vp"
ohos:height="40vp"
ohos:top_margin="40vp"
ohos:layout_alignment="horizontal_center"
ohos:text="Request Microphone permission"
ohos:text_size="20vp"
ohos:text_color="#ffffffff"
ohos:background_element="$graphic:bg"/>
<Button
ohos:id="$+id:get_calendar_permission"
ohos:width="300vp"
ohos:height="40vp"
ohos:top_margin="40vp"
ohos:layout_alignment="horizontal_center"
ohos:text="Request Calendar permission"
ohos:text_size="20vp"
ohos:text_color="#ffffffff"
ohos:background_element="$graphic:bg"/>
<Button
ohos:id="$+id:get_storage_permission"
ohos:width="300vp"
ohos:height="40vp"
ohos:top_margin="40vp"
ohos:layout_alignment="horizontal_center"
ohos:text="Request Storage permission"
ohos:text_size="20vp"
ohos:text_color="#ffffffff"
ohos:background_element="$graphic:bg"/>
</DirectionalLayout>
业务逻辑
1.config.json
首先要在config.json声明需要用户授权哪些权限,具体是在abilities 数组块下面添加reqPermissions数组
{
"app": {
"bundleName": "ohos.samples.permissionapp",
"version": {
"code": 2000000,
"name": "2.0"
}
},
"deviceConfig": {},
"module": {
"package": "ohos.samples.permissionapp",
"name": ".MainAbility",
"reqCapabilities": [
"video_support"
],
"deviceType": [
"default"
],
"distro": {
"deliveryWithInstall": true,
"moduleName": "entry",
"moduleType": "entry",
"installationFree": false
},
"abilities": [
{
"name": "ohos.samples.permissionapp.MainAbility",
"icon": "$media:icon",
"label": "$string:app_name",
"orientation": "portrait",
"visible": true,
"formsEnabled": false,
"description": "$string:mainability_description",
"type": "page",
"launchType": "standard",
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"reqPermissions": [
{
"name": "ohos.permission.LOCATION"
},
{
"name": "ohos.permission.MICROPHONE"
},
{
"name": "ohos.permission.READ_CALENDAR"
},
{
"name": "ohos.permission.READ_USER_STORAGE"
}
]
}
}
当然,你也可以把config.json切换至可视化窗口,这样可以根据提示进行添加。
2.MainAbility.java
该类重写了onRequestPermissionsFromUserResult方法,用来显示用户授权结果
/**
* 重写该方法,用户进行授权或拒绝授权后,会执行该方法,进行结果显示
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsFromUserResult(int requestCode, java.lang.String[] permissions,
int[] grantResults) {
if (permissions == null || permissions.length == 0 || grantResults == null || grantResults.length == 0) {
return;
}
LogUtil.debug(TAG,
"requestCode: " + requestCode + ", permissions:" + Arrays.toString(permissions) + ", grantResults: "
+ Arrays.toString(grantResults));
//显示授权结果
if (grantResults[0] == IBundleManager.PERMISSION_GRANTED) {
showTips(this, "Permission granted");
} else {
showTips(this, "Permission denied");
}
}
private void showTips(Context context, String message) {
new ToastDialog(context).setContentText(message).show();
}
3.MainAbilitySlice.java
该类实现了Component.ClickedListener接口,重写onClick方法,用来实现根据不同按钮组件的监听处理,
例如:点击获取位置授权按钮,调用accessLocation()方法
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_main_ability_slice);
//设置按钮的点击监听事件
findComponentById(ResourceTable.Id_get_location_permission).setClickedListener(this);
findComponentById(ResourceTable.Id_get_microphone_permission).setClickedListener(this);
findComponentById(ResourceTable.Id_get_calendar_permission).setClickedListener(this);
findComponentById(ResourceTable.Id_get_storage_permission).setClickedListener(this);
}
@Override
public void onClick(Component component) {
switch (component.getId()) {
//如果是获取位置的按钮组件
case ResourceTable.Id_get_location_permission:
accessLocation();
break;
case ResourceTable.Id_get_microphone_permission:
accessMicrophone();
break;
case ResourceTable.Id_get_calendar_permission:
accessCalendar();
break;
case ResourceTable.Id_get_storage_permission:
accessStorage();
break;
default:
LogUtil.warn(TAG, "Ignore click for component: " + component.getId());
}
}
定义请求权限的通用方法 和 showTips方法。
/**
*
* @param permission 权限名称,系统定义
* @param requestCode 要传递给ability的权限码,用户自定义的
*/
private void requestPermission(String permission, int requestCode) {
//检查当前进程是否具有给定的权限
if (verifySelfPermission(permission) == IBundleManager.PERMISSION_GRANTED) {
showTips(this, "Permission already obtained");
return;
}
//与权限管理模块确认是否需要请求提示来授予某个权限。
if (!canRequestPermission(permission)) {
showTips(this, "Cannot request Permission");
LogUtil.error(TAG, "Cannot request Permission");
return;
}
//从系统请求某些权限。
// 此方法用于权限请求,这是一种异步方法,
// 执行时会回调Ability.onRequestPermissionsFromUserResult(int, String[], int[])方法。
requestPermissionsFromUser(new String[] {permission}, requestCode);
}
/**
* 显示操作结果的小提示
* @param context
* @param message
*/
private void showTips(Context context, String message) {
new ToastDialog(context).setContentText(message).show();
}
通过业务来触发几种权限的具体方法
获取位置
private void accessLocation() {
try {
new Locator(this).getCachedLocation();
showTips(this, "Location access succeeded.");
} catch (SecurityException e) {
//如果没有ohos.permission.LOCATION 权限会抛出SecurityException异常,那就请求授权
requestPermission(SystemPermission.LOCATION, Constants.PERM_LOCATION_REQ_CODE);
}
}
麦克风录音,需要用到 AudioStreamInfo / AudioCapturerInfo / AudioCapturer
/**
* 录音权限
*/
private void accessMicrophone() {
//提供音频流信息
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();
final Context context = this;
try {
//为应用程序提供实现PCM录音和暂停功能的接口
AudioCapturer capturer = new AudioCapturer(audioCapturerInfo);
//开始录音
capturer.start();
showTips(this, "Start recording for 5 seconds.");
//定时5000ms
new Timer().schedule(new TimerTask() {
@Override
public void run() {
//停止录音
capturer.stop();
showTips(context, "Stopped recording.");
//获取绑定在UI线程的任务调度程序,在这个调度器上调度的任务按顺序执行,
//不要在UI线程上使用这个调度器运行同步任务,否则会造成死锁。
context.getUITaskDispatcher().asyncDispatch(() -> {
showTips(context, "Stopped recording.");
});
}
}, AUDIO_RECORDING_TIME);
} catch (IllegalArgumentException | SecurityException e) {
//权限问题会抛出SecurityException异常,请求麦克风权限
requestPermission(SystemPermission.MICROPHONE, Constants.PERM_RECORD_AUDIO_REQ_CODE);
}
}
获取日历数据
/**
* 获取日历数据
*/
private void accessCalendar() {
//提供用于创建、添加、查询、修改和删除日历数据的实用程序。
CalendarDataHelper calendarDataHelper = CalendarDataHelper.creator(this, Events.class);
try {
//查询日历数据
calendarDataHelper.query(0, new String[] {EventsColumns.ACC_ID, EventsColumns.TITLE});
showTips(this, "Calendar access succeeded.");
} catch (DataAbilityRemoteException | SecurityException e) {
//权限问题会抛出SecurityException异常,进行捕获处理
requestPermission(SystemPermission.READ_CALENDAR, Constants.PERM_READ_CALENDAR_REQ_CODE);
}
}
获取Media存储数据,
如果发生远程进程异常会抛出DataAbilityRemoteException 异常
/**
* 获取Media存储数据
*/
private void accessStorage() {
//帮助应用程序访问数据。
DataAbilityHelper helper = DataAbilityHelper.creator(this);
try {
//查询数据库生成的数据库结果集
ResultSet resultSet = helper.query(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI,
new String[] {AVStorage.Images.Media.ID}, null);
//查询结果数量
int count = resultSet.getRowCount();
LogUtil.info(TAG, "Images count: " + count);
if (count == 0) {
//请求权限
requestPermission(SystemPermission.READ_USER_STORAGE, Constants.PERM_READ_STORAGE_REQ_CODE);
return;
}
showTips(this, "Image access succeeded");
} catch (DataAbilityRemoteException e) {
//如果发生远程进程异常,则抛出此异常
LogUtil.info(TAG, "get images failed: " + e.getLocalizedMessage());
}
}
这就是所有的业务逻辑了
4.Constants.java
定义权限请求码常量,
这里要说明的是权限名称是系统定义好的,在SystemPermission中有很多,用的时候去查看就好。自定义的权限请求码是为了满足业务逻辑的需要。
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ohos.samples.permissionapp.utils;
/**
* Constants
* 定义权限请求码
*/
public class Constants {
/**
* Location permission request code
*/
public static final int PERM_LOCATION_REQ_CODE = 1000;
/**
* Contacts Read permission request code
*/
public static final int PERM_READ_CONTACTS_REQ_CODE = 1001;
/**
* Microphone permission request code
*/
public static final int PERM_RECORD_AUDIO_REQ_CODE = 1002;
/**
* Calendar read permission request code
*/
public static final int PERM_READ_CALENDAR_REQ_CODE = 1003;
/**
* Image/Videos Read permission request code
*/
public static final int PERM_READ_STORAGE_REQ_CODE = 1004;
private Constants() {
}
}
效果展示
用ScreenToGif录制了gif,小工具还挺好用的。
可以去应用管理查看应用的授权信息
完整代码
下载附件