介绍
通过AbilityAccessCtrl动态向用户申请“允许不同设备间的数据交换”的权限,使用设备管理实例获取周边不可信设备列表。
说明: 查询周边不可信设备之前,请确保本设备与周边设备未进行配对。如果已配对,则恢复出厂设置之后重新查询。
相关概念
- 访问控制权限申请:应用的APL(Ability Privilege Level)等级分为normal、system_basic和system_core三个等级,默认情况下,应用的APL等级都为normal等级。权限类型分为system_grant和user_grant两种类型。应用可申请的权限项参见应用权限列表。
- 权限类型说明:根据授权方式的不同,权限类型可分为system_grant(系统授权)和user_grant(用户授权)。
- 应用ALP等级说明:元能力权限等级APL(Ability Privilege Level)指的是应用的权限申请优先级的定义,不同APL等级的应用能够申请的权限等级不同。
- 应用权限列表:在申请目标权限前,建议开发者先阅读访问控制开发概述-权限的工作流程。对权限的工作流程有基本的了解后,再结合以下权限的具体说明,判断应用能否申请目标权限,提高开发效率。
- 设备管理实例:用于获取可信设备和本地设备的相关信息。在调用DeviceManager的方法前,需要先通过createDeviceManager构建一个DeviceManager实例dmInstance。
约束与限制
通过DevEco Studio自动下载的SDK均为public版本,public-SDK不支持开发者使用系统API。本篇Codelab使用的DeviceManager(设备管理实例)依赖于系统API,需下载full-SDK并替换工具自动下载的public-SDK。具体操作可参考指南《如何替换full-SDK》。
相关权限
本篇Codelab需要在配置文件module.json5里添加允许不同设备间的数据交换权限:ohos.permission.DISTRIBUTED_DATASYNC。
环境搭建
软件要求
- DevEco Studio版本:DevEco Studio 3.1 Release。
- OpenHarmony SDK版本:API version 9。
硬件要求
- 开发板类型:润和RK3568开发板。
- OpenHarmony系统:3.2 Release。
环境搭建
完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:
- 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:
2.搭建烧录环境。
- 完成DevEco Device Tool的安装
- 完成RK3568开发板的烧录
3.搭建开发环境。
- 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
- 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
- 工程创建完成后,选择使用真机进行调测。
代码结构解读
本篇Codelab只对核心代码进行讲解。
├──entry/src/main/ets // 代码区
│ ├──common
│ │ ├──constants
│ │ │ └──CommonConstants.ets // 公共常量类
│ │ └──util
│ │ ├──DeviceListUtil.ets // 设备查询工具类
│ │ ├──Logger.ets // 日志管理工具类
│ │ └──PermissionUtil.ets // 权限申请工具类
│ ├──entryability
│ │ └──EntryAbility.ts // 程序入口类
│ ├──pages
│ │ └──HomePage.ets // 主页面
│ └──view
│ ├──DeviceBean.ets // 设备信息类
│ ├──DeviceItem.ets // 设备信息对象
│ ├──DeviceListDialog.ets // 设备列表展示弹框
│ └──NoPermissionDialog.ets // 无权限弹框
└──entry/src/main/resource // 应用静态资源目录
页面设计
页面分为授权图片、授权说明、查询内网设备按钮三部分,效果如图所示:
// HomePage.ets
struct HomePage {@State authorizedImage: Resource = $r('app.media.ic_unauthorized');@State permissionDescribe: Resource = $r('app.string.no_permission');noPermissionDialog: CustomDialogController = new CustomDialogController({builder: NoPermissionDialog(),autoCancel: false,alignment: DialogAlignment.Bottom,offset: {dx: CommonConstants.DIALOG_OFFSET_X,dy: CommonConstants.DIALOG_OFFSET_Y}});deviceListDialog: CustomDialogController = new CustomDialogController({builder: DeviceListDialog(),autoCancel: false,alignment: DialogAlignment.Bottom,offset: {dx: CommonConstants.DIALOG_OFFSET_X,dy: CommonConstants.DIALOG_OFFSET_Y}})...build() {Column() {Image(this.authorizedImage)...Text(this.permissionDescribe)...Column() {Button($r('app.string.button_text'), { type: ButtonType.Capsule, stateEffect: true })...}...}...}
}
授权状态为未授权时,点击查询内网设备按钮打开未申请权限提示的弹框,效果如图所示:
// NoPermissionDialog.ets
@CustomDialog
export struct NoPermissionDialog {controller: CustomDialogController;build() {Column() {Text($r('app.string.no_permission_title'))...Text($r('app.string.clear_permission'))...Text($r('app.string.dialog_confirm'))....onClick(() => {this.controller.close();})}...}
}
授权状态为已授权时,点击查询内网设备按钮打开设备查询列表弹框,效果如图所示:
// DeviceListDialog.ets
@CustomDialog
export struct DeviceListDialog {private deviceListUtil: DeviceListUtil = new DeviceListUtil();@State deviceList: Array<DeviceBean> = [];controller: CustomDialogController;...build() {Column() {Text($r('app.string.device_list'))...Column() {if (this.deviceList.length === 0) {Text($r('app.string.no_device'))...} else {Column() {List() {ForEach(this.deviceList, (item: DeviceBean, index: number) => {ListItem() {DeviceItem({item: item, index: index});}}, (item: DeviceBean) => JSON.stringify(item))}}...}}...Text($r('app.string.dialog_confirm'))....onClick(() => {this.deviceListUtil.stopDeviceDiscovery();this.controller.close();})}...}
}
权限申请
首次进入主页面弹出授权弹框,点击禁止按钮不会授权,点击允许按钮进行授权,再次进入首页不会出现授权弹框。
// HomePage.ets
struct HomePage {...async aboutToAppear() {let result = await PermissionUtil.applyPermission();if (result === 0) {this.permissionDescribe = $r('app.string.has_permission');this.authorizedImage = $r('app.media.ic_authorized');} else {this.permissionDescribe = $r('app.string.no_permission');}}...
}// PermissionUtil.ets
async applyPermission() {let atManager = abilityAccessCtrl.createAtManager();let data = await atManager.requestPermissionsFromUser(getContext(this), [CommonConstants.PERMISSION]);let grantStatus: Array<number> = data.authResults;let length: number = grantStatus.length;if (length === 0) {return 1;}return grantStatus[0];
}
主页面点击查询内网设备按钮时,先查询是否授权,根据授权状态打开对应的弹框(未授权:打开未授权弹框;已授权:打开设备查询列表弹框)。
// HomePage.ets
struct HomePage {...build() {Column() {...Column() {Button($r('app.string.button_text'), { type: ButtonType.Capsule, stateEffect: true })....onClick(async () => {let result = await PermissionUtil.checkPermission();if(result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {this.deviceListDialog.open();} else {this.noPermissionDialog.open();}})}...}...}
}// PermissionUtil.ets
async checkPermission() {let atManager = abilityAccessCtrl.createAtManager();let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;let tokenId: number = 0;try {let bundleInfo: bundleManager.BundleInfo =await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;tokenId = appInfo.accessTokenId;} catch (err) {Logger.error(TAG, 'getBundleInfoForSelf failed, code is ${err.code}, message is ${err.message}');}try {grantStatus = await atManager.checkAccessToken(tokenId, CommonConstants.PERMISSION);} catch (err) {Logger.error(TAG, 'checkAccessToken failed, code is ${err.code}, message is ${err.message}');}return grantStatus;
}
设备查询
先创建dmInstance实例,再注册发现设备(deviceFoundOn)的回调方法,最后调用startDeviceDiscovery方法查询周边设备;当查询到设备之后调用deviceFoundOn方法处理设备信息。
//DeviceListDialog.ets
export struct DeviceListDialog {...aboutToAppear() {this.deviceListUtil.initDmInstance((data: DeviceInfoInterface) => {...});}...
}// DeviceListUtil.ets
// 创建dmInstance实例
initDmInstance(dealDeviceInfo: Function) {this.dealDeviceInfo = dealDeviceInfo;try {deviceManager.createDeviceManager(getContext(this).applicationInfo.name, (err, data) => {if (err) {Logger.error(TAG, 'createDeviceManager errCode:' + err.code + ',errMessage:' + err.message);return;}this.dmInstance = data;this.deviceFoundOn();this.startDeviceDiscovery();});} catch (err) {Logger.error(TAG, 'createDeviceManager err=' + JSON.stringify(err));}
}// DeviceListUtil.ets
// 注册发现设备回调方法
deviceFoundOn() {try {if (this.dmInstance !== undefined) {this.dmInstance.on('deviceFound', (data) => {if (this.dealDeviceInfo !== undefined) {this.dealDeviceInfo(data);}});}} catch (err) {Logger.error(TAG, 'deviceFoundOn err:' + JSON.stringify(err));}
}// DeviceListUtil.ets
// 发现周边设备方法
startDeviceDiscovery() {this.subscribeId = Math.floor(Math.random() * CommonConstants.RANDOM_ONE + CommonConstants.RANDOM_TWO);let subscribeInfo: SubscribeInfoInterface = {subscribeId: this.subscribeId,mode: CommonConstants.MODE,medium: 0,freq: CommonConstants.FREQ,isSameAccount: false,isWakeRemote: true,capability: 1};try {if (this.dmInstance !== undefined) {this.dmInstance.startDeviceDiscovery(subscribeInfo);}} catch (err) {Logger.error(TAG, 'startDeviceDiscovery err:' + JSON.stringify(err));}
}
总结
您已经完成了本次Codelab的学习,并了解到以下知识点:
- 访问控制授权申请。
- 权限类型说明。
- 如何获取周边设备。
为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频》
HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等.....视频教程
鸿蒙生态应用开发白皮书V2.0PDF:
获取完整版白皮书方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF》
鸿蒙 (Harmony OS)开发学习手册
一、入门必看
- 应用开发导读(ArkTS)
- ……
二、HarmonyOS 概念
- 系统定义
- 技术架构
- 技术特性
- 系统安全
- ........
三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》
- 基本概念
- 构建第一个ArkTS应用
- ……
四、开发基础知识
- 应用基础知识
- 配置文件
- 应用数据管理
- 应用安全管理
- 应用隐私保护
- 三方应用调用管控机制
- 资源分类与访问
- 学习ArkTS语言
- ……
五、基于ArkTS 开发
- Ability开发
- UI开发
- 公共事件与通知
- 窗口管理
- 媒体
- 安全
- 网络与链接
- 电话服务
- 数据管理
- 后台任务(Background Task)管理
- 设备管理
- 设备使用信息统计
- DFX
- 国际化开发
- 折叠屏系列
- ……
更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册》