HarmonyOS实战开发-为应用添加运行时权限

news2024/11/25 12:43:26

介绍

通过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开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。以3.2 Release版本为例:

2.搭建烧录环境。

  1. 完成DevEco Device Tool的安装
  2. 完成RK3568开发板的烧录

3.搭建开发环境。

  1. 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
  2. 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”)。
  3. 工程创建完成后,选择使用真机进行调测。

代码结构解读

本篇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的学习,并了解到以下知识点:

  1. 访问控制授权申请。
  2. 权限类型说明。
  3. 如何获取周边设备。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频

HarmonyOS教学视频:语法ArkTS、TypeScript、ArkUI等.....视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取完整版白皮书方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF》

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. ……

二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全
  5. ........

三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1563612.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

税务知识竞赛活动方案

为了提高税务工作人员的业务技能和知识&#xff0c;现在全市范围开展税务知识竞赛&#xff0c;希望通过“以赛促学”&#xff0c;在税务系统掀起一场税务知识学习热潮。下面是本次竞赛的活动方案。 1、第一轮&#xff1a;争分夺秒。选择题或判断题&#xff0c;每位参赛选手按编…

LiDAR和Camera融合的BEV感知算法-BEVFusion

0. 简述 本次给大家讲解一篇非常经典的融合工作叫 BEVFusion&#xff0c;我们依旧从算法动机&开创性思路、主体结构、损失函数以及性能对比四个方面展开 BEVFusion 有两篇文章&#xff0c;本次主要讲解的是阿里和北大的&#xff1a;BEVFusion: A Simple and Robust LiDAR-…

Docker容器与虚拟化技术:OpenEuler 部署 Docker UI

目录 一、实验 1.环境 2.OpenEuler 部署 docker-compose-ui 2.OpenEuler 部署 docker ui 3.使用cpolar内网穿透 二、问题 1.docker run -w 的作用 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统架构版本IP备注LinuxopenEuler22.03 LTS SP2 192.168…

MQ

目录 MQ优点 异步 解耦 削峰填谷 mq的缺点 MQ常见的几种模式 Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别 MQ优点 mq是一种常见的中间件&#xff0c;在项目中经常用到&#xff0c;它具有异步、解耦、削峰填谷的作用。 异步 比如下单流程&#xff0c;A服务—>B服务&a…

SiameseRPN原理详解(个人学习笔记)

参考资源&#xff1a; 视觉目标跟踪SiamRPNSiameseRPN详解CVPR2018视觉目标跟踪之 SiameseRPN 目录&#xff09; 1. 模型架构1.1 Siamese Network1.2 RPN 2. 模型训练2.1 损失函数2.2 端到端训练2.3 正负样本选择 3. 跟踪阶段总结 SiamRPN是在SiamFC的基础上进行改进而得到的一…

FreeRTOS_day3:2024/4/2

1.总结任务调度算法之间的区别&#xff0c;重新实现一遍任务调度算法的代码。 (1)抢占式调度&#xff1a;高优先级任务可以打断低优先级任务&#xff0c;用于任务优先级不同的任务 (2)时间片轮转&#xff1a;相同优先级的任务&#xff0c;分配相同的时间片(一般为 1ms)&#x…

解决GNU Radio+USRP实现OFDM收发在接收端存在误码问题

文章目录 前言一、OFDM 收发流程1、OFDM 收端流程2、OFDM 收端流程 二、问题所在1、find_trigger_signal 函数解读2、general_work 函数3、问题所在 三、修改源码四、运行结果1、频谱2、传输数据测试 五、调试小技巧六、资源自取 前言 在使用 GNU Radio 时使用官方例程搭建 GN…

游戏引擎之高级动画技术

一、动画混合 当我们拥有各类动画素材&#xff08;clips&#xff09;时&#xff0c;要将它们融合起来成为一套完整的动画。 最经典的例子就是从走的动画自然的过渡到跑的动画。 1.1 线性插值 不同于上节课的LERP&#xff08;同一个clip内不同pose之间&#xff09;&#xff…

学习使用echats因xAxis值过多,可以滚动的柱状图解决方案

学习使用echats因xAxis值过多&#xff0c;可以滚动的柱状图解决方案 效果图柱状图代码关键代码 效果图 柱状图代码 function echarts() {// 基于准备好的dom&#xff0c;初始化echarts实例var myChart echarts.init(document.getElementById(echart4));let xaxisData [1, 2,…

HTML常用标签-最基础的标签

从本篇开始&#xff0c;我们围绕HTML原生标签开始&#xff0c;围绕整个前端三剑客进行&#xff0c;将进行一个大致的介绍和案例展示&#xff0c;没有啥技术含量&#xff0c;只是把学习前端的时候&#xff0c;案例全部展示出来&#xff0c;作为一个实时记录&#xff0c;或者说回…

Redis高可用(持久化)

目录 一、Redis高可用 1. Redis高可用概述 2. Redis高可用策略 二、Redis持久化 1. Redis持久化的功能 2. Redis持久化的两种方式 3. RDB持久化 3.1 触发条件 3.1.1 手动触发 3.1.2 自动触发 ① 配置方式 ②其他自动触发机制 3.2 执行流程 3.3 启动时加载 4. AOF…

jnpf3.6私有化部署

文件内容 project web > 特别说明&#xff1a;源码、JDK、MySQL、Redis等安装或存放路径禁止包含中文、空格、特殊字符等## 一 技术栈- 主框架&#xff1a;Spring Boot Spring Framework - 持久层框架&#xff1a;MyBatis-Plus - 数据库连接池&#xff1a;Alibaba Druid -…

商场促销--策略模式

1.1 商场收银软件 package com.lhx.design.pattern.test;import java.util.Scanner;public class Test {public static void main(String[] args){System.out.println("**********************************************"); System.out.println("《大话设计模式…

坦白局:PMP真的是智商税吗?

近些年报考PMP认证的学员越来越多&#xff0c;PMP全球持证人数已经突破百万了&#xff0c;据PMI统计&#xff0c;IT行业近50%人士都持有PMP证书&#xff0c;因此也有很多学员在思考&#xff0c;PMP持证人员这么多&#xff0c;PMP是不是都已经烂大街了&#xff1f;证书还有含金量…

看完不会来揍我 | R包的下载与安装 | 再也没有一个包可以逃出你的手掌心啦

好久不见&#xff01;非常抱歉有一段时间没有更新正经内容啦&#xff01;主要是最近接了一个项目和一个一对一指导&#xff0c;实在是精力有限&#xff0c;又不想随便写几篇应付大家。毕竟&#xff0c;咱们主打高质量嘛&#xff01;来&#xff01;大声喊出来&#xff01; 「要知…

英伟达智算先锋训练,冲刺智算时代实战

随着数字经济的深入发展&#xff0c;智能算力作为关键生产力&#xff0c;其规模在2022年已达到268.0 EFLOPS&#xff0c;并预计到2028年将增长至2769 EFLOPS&#xff0c;显示出强劲的发展势头。在2024年政府工作报告中&#xff0c;也首次提出了“人工智能”行动&#xff0c;强调…

Golang 内存管理和垃圾回收底层原理(二)

一、这篇文章我们来聊聊Golang内存管理和垃圾回收&#xff0c;主要注重基本底层原理讲解&#xff0c;进一步实战待后续文章 垃圾回收&#xff0c;无论是Java 还是 Golang&#xff0c;基本的逻辑都是基于 标记-清理 的&#xff0c; 标记是指标记可能需要回收的对象&#xff0c…

Java设计之道:色即是空,空即是色

0.引子 我们的这个世界上&#xff0c;存在这么一种东西&#xff1a; 第一&#xff1a;它不占据任何3D之体积&#xff0c;即它没有Volume第二&#xff1a;它也不占据任何2D之面积&#xff0c;即它没有Area第三&#xff1a;它也不占据任何1D之长度&#xff0c;即它没有Length 总…

【容易不简单】love 2d Lua 俄罗斯方块超详细教程

源码已经更新在CSDN的码库里&#xff1a; git clone https://gitcode.com/funsion/love2d-game.git 一直在找Lua 能快速便捷实现图形界面的软件&#xff0c;找了一堆&#xff0c;终于发现love2d是小而美的原生lua图形界面实现的方式。 并参考相关教程做了一个更详细的&#x…