基于鸿蒙OS开发静脉输液智能监控系统(2)-开发环境搭建与项目构建

目录


1. HarmonyOS 平台概述

1.1 OpenHarmony vs HarmonyOS

HarmonyOS 生态中存在两个密切相关但定位不同的操作系统版本:OpenHarmonyHarmonyOS。理解二者的区别对于项目选型和技术决策至关重要。

1.1.1 OpenHarmony:开源基础

OpenHarmony 是由开放原子开源基金会(OpenAtom Foundation)托管的开源项目,是 HarmonyOS 的开源基础底座。它具备以下核心特征:

  • 完全开源:代码托管在 Gitee,遵循 Apache License 2.0 协议,任何人都可以自由使用、修改和分发
  • 社区驱动:由华为、润和软件、软通动力等多家企业共同参与共建,采用社区治理模式
  • 基础能力:提供了分布式软总线、分布式数据管理、分布式任务调度等核心分布式能力,以及基础的 UI 框架、应用框架和安全机制
  • 设备适配:主要面向轻量级 IoT 设备(如智能手表、智能家居)、小型设备以及部分标准设备
  • SDK 独立性:OpenHarmony 提供独立的 SDK,可通过 DevEco Studio 下载使用,API 以 @ohos.* 命名空间为主
1.1.2 HarmonyOS:华为商业版

HarmonyOS 是华为基于 OpenHarmony 内核打造的商业操作系统,在开源基础上增加了大量华为专有的商业能力和服务:

  • 闭源扩展:在 OpenHarmony 基础上叠加了华为专有的闭源模块,包括 HMS Core(华为移动服务)、华为云服务集成、华为账号体系等
  • 商业服务:集成华为应用市场(AppGallery)、华为钱包、华为支付等商业生态
  • 高级 API:提供了 @kit.* 命名空间下的高级 Kit API,如 @kit.AbilityKit@kit.ArkData@kit.CameraKit 等,这些 API 在纯 OpenHarmony 环境中不可用
  • 安全增强:增加了更严格的安全审核机制、应用签名校验、数字盾服务等企业级安全能力
  • 多设备协同:深度融合华为"1+8+N"全场景战略,实现手机、平板、PC、手表、车机等设备间的无缝协同
  • 应用市场分发:通过华为 AppGallery 进行应用分发,需要遵循华为的上架审核规范
1.1.3 IVGuard 的选择

IVGuard 项目选择基于 HarmonyOS 商业版进行开发,主要原因如下:

维度 OpenHarmony HarmonyOS IVGuard 选择理由
API 丰富度 基础 API 基础 + Kit API 需要 @kit.CameraKit(相机监控)、@kit.ArkData(数据持久化)等 Kit API
设备适配 IoT / 小型设备为主 全品类设备 目标运行在华为手机/平板上
分发渠道 侧载安装 AppGallery 计划通过 AppGallery 分发给医院和患者
NFC 支持 基础 NFC API 增强的 NFC 标签读取 需要读取 NFC 贴纸中的药物信息
通知服务 基础通知 增强通知 + 后台保活 输液结束提醒需要可靠的后台通知

1.2 API 版本演进

HarmonyOS 的 API Level 体系经历了从 API 9 到 API 24 的重大演进,每次版本迭代都带来了新的能力和架构变化。

1.2.1 关键版本里程碑
API 9 (OpenHarmony 3.2)  ─── Stage 模型成熟,ArkUI 声明式范式确立
    │
API 10 (OpenHarmony 4.0) ─── ArkTS 语言正式定名,strict mode 引入
    │
API 11 (HarmonyOS 4.0)   ─── @kit 命名空间引入,Kit 化 API 架构
    │
API 12 (HarmonyOS 4.1)   ─── ArkTS 严格模式全面推行,性能优化
    │
API 13-14                 ─── 过渡版本,安全增强
    │
API 24 (SDK 6.1.1)       ─── HarmonyOS 5.0,IVGuard 使用版本
1.2.2 API 9 到 API 12 的重大变化

API 9 是 Stage 模型(Stage Model)走向成熟的标志版本。在这个版本中:

  • Stage 模型成为推荐的应用开发模型,替代了早期的 FA(Feature Ability)模型
  • ArkUI 声明式开发范式确立,@Component@State@Entry 等装饰器成为标准
  • module.json5 替代了 FA 模型中的 config.json,成为模块配置的标准文件
  • UIAbility 替代了 Ability,生命周期管理更加清晰

API 10 带来了 ArkTS 语言的正式定名:

  • ArkTS 作为 TypeScript 的超集被正式命名,增加了静态类型约束和性能优化
  • @ohos. API* 命名空间基本定型
  • hvigor 构建系统替代了早期的 Gradle 构建方案

API 11 引入了 Kit 化 API 架构:

  • @kit. 命名空间*引入,将 API 按 Kit 分组,如 @kit.AbilityKit@kit.ArkUI@kit.ArkData
  • 旧的 @ohos.* API 仍可使用,但新 API 优先以 Kit 形式提供
  • 引入了更严格的安全沙箱机制

API 12 推行了 ArkTS 严格模式:

  • arkts-no-standalone-this:禁止在 @Component 外使用 this
  • arkts-no-any-unknown:禁止使用 anyunknown 类型
  • arkts-no-structural-type:禁止结构类型,强制使用类(class)
  • arkts-no-destructuring:限制解构赋值的使用
  • 这些限制确保了 ArkTS 代码在方舟编译器(Ark Compiler)上的高效执行
1.2.3 API Level 24 的核心变化

IVGuard 使用的 API Level 24(对应 SDK 版本 6.1.1(24))是 HarmonyOS 5.0 的 API 版本,带来了以下重要变化:

Ability Kit 增强

  • AbilityStage 组件管理器新增 AbilityStage 即将创建第一个 Ability 的回调
  • 新增进程从应用快照启动时的回调
  • Ability 上次退出的信息字段新增支持获取退出原因

ArkTS 语言增强

  • 新增 enableLocalHandleDetection 接口,保证 EventHandler 和 libuv 机制的任务在 scope 范围内执行,避免内存泄漏
  • XML 解析新增支持 XmlSAXHandler
  • 虚拟机维测能力增强:新增获取所有虚拟机线程的堆内存信息
  • taskpool 的 execute 方法增强,支持指定任务超时时长

Camera Kit 专业能力扩展(对 IVGuard 的视觉监控功能尤其重要):

  • 新增闪光灯状态变化事件回调的订阅/取消订阅
  • 新增光学防抖(OIS)相关参数的查询和设置
  • 相机设备定义中新增镜头焦距、等效焦距、最小对焦距离等详细信息
  • 新增逻辑摄像头管理能力
  • 相机曝光设置增强:新增自动曝光和手动曝光能力
  • 新增手动对焦、手动 ISO 感光度、物理光圈设置能力
  • 新增延迟预览输出对象

ArkUI 组件增强

  • 自定义组件支持跨 Ability 迁移
  • Tabs 组件新增支持嵌套滚动
  • 新增动态布局容器组件,支持运行时动态切换布局算法
  • Text 组件新增支持根据坐标获取最近字符位置
  • 新增拖拽异步通知接口
  • 新增 onNeedSoftkeyboard 回调,支持焦点转移后不关闭软键盘

ArkData 数据能力

  • 新增创建或打开关系型数据库的同步方法
1.2.4 IVGuard 的 API 版本配置

在 IVGuard 项目的 build-profile.json5 中,API 版本配置如下:

{
  "app": {
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}
  • targetSdkVersion:指定编译时使用的 SDK 版本,决定了可以使用的 API 集合
  • compatibleSdkVersion:指定应用最低兼容的 SDK 版本,低于此版本的设备无法安装应用
  • IVGuard 将两者都设为 6.1.1(24),意味着应用仅在 HarmonyOS 5.0 及以上版本运行,不需要向后兼容旧版本

1.3 ArkTS 在 HarmonyOS 生态中的定位

1.3.1 ArkTS 是什么

ArkTS 是华为基于 TypeScript 扩展的编程语言,是 HarmonyOS 应用开发的首选和推荐语言。它在 TypeScript 的基础上增加了以下关键特性:

  • 静态类型强化:通过严格模式(strict mode)禁止 anyunknown 等动态类型,确保编译期类型安全
  • 声明式 UI:通过 @Component@Builder 等装饰器和声明式语法,实现 UI 与状态的绑定
  • 高性能编译:方舟编译器(Ark Compiler)将 ArkTS 直接编译为机器码,跳过传统 JS 引擎的解释执行和 JIT 编译
  • 并发模型:通过 TaskPool 和 Worker 实现高效的多线程并发

ArkTS 与 TypeScript 的关系可以概括为:

TypeScript(超集)──→ ArkTS(约束子集 + 扩展)
                         │
                         ├── 增加的:@Component/@State/@Entry 装饰器
                         │           声明式 UI 语法
                         │           Sendable 类型系统
                         │
                         └── 约束的:禁止 any/unknown
                                    禁止结构类型
                                    限制解构赋值
                                    禁止 standalone this
1.3.2 ArkUI 声明式框架

ArkUI 是 HarmonyOS 的声明式 UI 开发框架,其核心设计理念是状态驱动 UI 更新。当状态变量发生变化时,框架自动重新渲染依赖该状态的 UI 组件,无需手动操作 DOM。

ArkUI 的核心装饰器:

装饰器 作用 使用场景
@Entry 标记页面入口组件 每个 page 文件的根组件
@Component 标记自定义组件 所有自定义组件必须添加
@State 响应式状态变量 组件内部状态,变化触发 UI 更新
@Prop 单向数据传递 父组件向子组件传递数据
@Link 双向数据绑定 父子组件间双向同步数据
@Provide/@Consume 跨层级数据传递 祖先组件与后代组件间通信
@Watch 状态变化监听 需要在状态变化时执行逻辑
@Builder 轻量 UI 复用 封装可复用的 UI 片段

以 IVGuard 的 Index 页面为例:

@Entry
@Component
struct Index {
  @State currentRole: string = ''

  aboutToAppear(): void {
    const context = getContext(this) as common.UIAbilityContext
    DataStore.init(context).then(() => {
      const settings = DataStore.loadSettings()
      this.currentRole = settings.role
    })
  }

  build() {
    Column() {
      Text('IVGuard')
        .fontSize(32)
        .fontWeight(FontWeight.Bold)
        .fontColor('#2196F3')
    }
  }
}

currentRole@State 标记后,任何对其的修改都会自动触发 build() 方法的重新执行,更新对应的 UI 组件。

1.3.3 ArkTS 严格模式

从 API 12 开始,ArkTS 严格模式成为默认编译选项。严格模式下的主要限制包括:

arkts-no-any-unknown:禁止使用 anyunknown 类型。所有变量必须有明确的类型声明。

// 错误
let data: any = fetchData()
let result: unknown = parseData()

// 正确
let data: string = fetchData()
let result: Record<string, Object> = parseData()

arkts-no-standalone-this:禁止在 @Component 外部使用 this。在 ArkTS 中,this 只能在 struct 方法内使用。

// 错误
function helper() {
  console.log(this.name)
}

// 正确
@Component
struct MyComponent {
  @State name: string = ''
  build() {
    Text(this.name)
  }
}

arkts-no-structural-type:禁止使用结构类型,必须使用类(class)进行类型定义。

// 错误 - 使用对象字面量类型
type Person = { name: string; age: number }

// 正确 - 使用 class
class Person {
  name: string = ''
  age: number = 0
}

arkts-no-destructuring:限制解构赋值的使用,仅允许在特定场景下使用。

// 错误 - 在 build() 方法中使用 const 解构
build() {
  const { width, height } = this.dimensions
}

// 正确 - 直接访问属性或使用 @State
build() {
  Text(`${this.dimensions.width}`)
}

1.4 HarmonyOS 5.0 新特性

HarmonyOS 5.0(对应 API Level 24)是 2024-2025 年华为发布的最新大版本,带来了以下重要特性:

1.4.1 纯血鸿蒙(Pure HarmonyOS)

HarmonyOS 5.0 彻底移除了 Android AOSP 代码,成为完全自主的操作系统:

  • 不再支持 Android 应用兼容运行
  • 所有应用必须使用 ArkTS/ArkUI 原生开发
  • 系统更加轻量、安全、高效
  • 对 IVGuard 而言,这意味着必须使用原生 ArkTS 开发,不存在 Android 兼容的捷径
1.4.2 原生智能

HarmonyOS 5.0 将 AI 能力深度集成到系统层面:

  • 小艺智慧助手升级,支持更复杂的自然语言交互
  • AI 文本处理:系统级文本摘要、翻译、润色能力
  • AI 图像处理:图像识别、图像生成、图像增强能力
  • 对 IVGuard 而言,可以利用系统级 AI 能力增强输液瓶液位的视觉识别精度
1.4.3 原生安全

安全能力的全面增强:

  • 应用签名校验更严格,未签名应用无法安装
  • 数字盾服务扩展到 Tablet、PC 设备
  • 代码签名信息查询功能,可获取设备上已签名文件的签名信息
  • 系统安全事件监控:进程提权、异常调试、异常崩溃等事件监控
  • 对 IVGuard 而言,涉及患者数据的处理必须遵循这些安全规范
1.4.4 原生精致

UI/UX 能力的全面提升:

  • 动态布局容器:支持运行时动态切换布局算法
  • 自定义组件跨 Ability 迁移:提升多窗口场景的用户体验
  • HDR 提亮效果:为组件内容添加高动态范围成像提亮
  • 分段按钮状态动画:更好的交互反馈

2. DevEco Studio 环境搭建

2.1 安装与配置

2.1.1 系统要求

DevEco Studio 是 HarmonyOS 应用开发的官方集成开发环境(IDE),基于 IntelliJ IDEA 社区版定制。

Windows 系统要求

项目 最低要求 推荐配置
操作系统 Windows 10 64 位 Windows 11 64 位
内存 8 GB 16 GB 及以上
硬盘空间 10 GB(IDE + SDK) 30 GB 及以上(含模拟器镜像)
分辨率 1280×800 1920×1080 及以上
CPU x86_64,4 核 x86_64,8 核及以上

macOS 系统要求

项目 最低要求 推荐配置
操作系统 macOS 10.14 macOS 12 及以上
内存 8 GB 16 GB 及以上
硬盘空间 10 GB 30 GB 及以上
架构 Intel / Apple Silicon Apple Silicon (M1/M2/M3)
2.1.2 下载与安装

DevEco Studio 的下载地址为华为开发者官网:https://developer.huawei.com/consumer/cn/deveco-studio/

安装步骤(Windows):

  1. 下载安装包 DevEco-Studio-xxx-windows.exe
  2. 双击运行安装程序,选择安装路径(路径中不要包含中文、空格和特殊字符
  3. 建议安装路径示例:F:\DevEco Studio(避免 C:\Program Files 等带空格的路径)
  4. 安装完成后首次启动,DevEco Studio 会自动引导下载 HarmonyOS SDK
  5. 在 SDK Setup 界面,选择 SDK 安装路径(建议与 IDE 分开存放,如 F:\HarmonyOS\Sdk
2.1.3 首次启动配置

首次启动 DevEco Studio 后,需要完成以下配置:

  1. 同意用户协议:阅读并同意 Huawei Developer Terms of Service
  2. SDK 下载:选择需要下载的 SDK 组件,至少包含:
    • HarmonyOS SDK(API 24)
    • OpenHarmony SDK(可选)
    • SDK Tools(包含 ohpm、hvigor 等工具链)
  3. Node.js 配置:DevEco Studio 内置了 Node.js 环境,无需单独安装
  4. ohpm 配置:OpenHarmony Package Manager 会随 SDK 自动安装
2.1.4 环境变量验证

安装完成后,可通过命令行验证关键工具是否可用:

# 检查 Node.js 版本(DevEco Studio 内置)
node --version
# 预期输出:v18.x.x 或更高

# 检查 ohpm 版本
ohpm --version

# 检查 hdc 版本(HarmonyOS Device Connector)
hdc version

# 检查 hvigorw 是否可执行(在项目目录下)
.\hvigorw.bat --version

2.2 SDK 管理

2.2.1 SDK 目录结构

HarmonyOS SDK 安装后的目录结构如下:

HarmonyOS/Sdk/
├── openharmony/              # OpenHarmony SDK
│   └── 24/                   # API Level
│       ├── native/           # Native 开发头文件和库
│       └── ets/              # ArkTS/JS API 声明文件
├── harmonyos/                # HarmonyOS SDK
│   └── 24/
│       ├── native/
│       └── ets/
└── toolchains/               # 工具链
    ├── hdc.exe               # HarmonyOS Device Connector
    ├── ohpm/                 # 包管理器
    └── hvigor/               # 构建工具
2.2.2 sdk-pkg.json 的作用

sdk-pkg.json 是 SDK 包的元数据描述文件,定义了每个 SDK 组件的版本、依赖关系和下载地址。其核心作用包括:

  • 版本管理:记录每个 SDK 组件的版本号和 API Level 对应关系
  • 依赖声明:定义组件间的依赖关系,确保安装完整性
  • 下载配置:指定组件的下载源和校验信息

当 DevEco Studio 的 SDK Manager 界面展示可用 SDK 列表时,实际上就是读取了远程的 sdk-pkg.json 来获取最新版本信息。

2.2.3 API 24 SDK 安装

IVGuard 需要 API Level 24 的 SDK,安装步骤如下:

  1. 打开 DevEco Studio
  2. 进入 File > Settings > HarmonyOS SDK
  3. 在 SDK Platforms 标签页中,勾选 API 24 (6.1.1)
  4. 在 SDK Tools 标签页中,确保以下工具已安装:
    • HarmonyOS SDK Build Tools
    • HarmonyOS SDK Platform Tools
    • Ohpm
    • Hvigor
  5. 点击 Apply 开始下载安装

安装完成后,可以在项目的 build-profile.json5 中确认 SDK 版本:

{
  "app": {
    "products": [
      {
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)"
      }
    ]
  }
}
2.2.4 SDK 版本切换

如果需要在多个 API Level 之间切换开发,需要注意:

  • targetSdkVersion 决定了编译时可用的 API 集合
  • compatibleSdkVersion 决定了应用可安装的最低系统版本
  • 降低 compatibleSdkVersion 后,需要确保代码中没有使用高版本才有的 API,否则低版本设备会崩溃
  • IVGuard 将两者都设为 API 24,意味着不需要处理低版本兼容问题

2.3 模拟器与真机调试

2.3.1 Local Emulator(本地模拟器)

Local Emulator 是运行在开发者本地电脑上的模拟器,使用硬件虚拟化技术(Windows 上使用 Hyper-V 或 Intel HAXM):

优点

  • 响应速度快,与本地网络无延迟
  • 不需要华为开发者账号
  • 支持断点调试、性能分析等全套开发工具

缺点

  • 需要电脑支持硬件虚拟化(CPU 支持且 BIOS 已开启)
  • 消耗本地内存和 CPU 资源(至少分配 4 GB 内存给模拟器)
  • 不支持某些需要真机硬件的功能(如 NFC、实际相机传感器)
  • 部分系统 API 行为与真机有差异

创建 Local Emulator 的步骤

  1. 打开 DevEco Studio,进入 Tools > Device Manager
  2. 点击 New Emulator 按钮
  3. 选择设备类型(Phone / Tablet / Wearable / TV)
  4. 选择系统镜像(API 24)
  5. 配置模拟器参数:名称、内存大小、存储大小
  6. 点击 Create 完成创建
  7. 点击启动按钮运行模拟器
2.3.2 Remote Simulator(远程模拟器)

Remote Simulator 是运行在华为云端的模拟器,通过网络连接使用:

优点

  • 不消耗本地计算资源
  • 无需本地硬件虚拟化支持
  • 系统版本与真机一致

缺点

  • 需要华为开发者账号登录
  • 网络延迟明显,操作体验不如本地模拟器
  • 有使用时长限制
  • 不支持某些硬件相关功能
2.3.3 真机调试

真机调试是最接近真实用户体验的调试方式,也是 IVGuard 项目推荐的调试方式(因为项目依赖相机、NFC 等硬件能力):

前置条件

  1. 华为手机/平板已升级到 HarmonyOS 5.0
  2. 设备已开启开发者模式:设置 > 关于手机 > 连续点击版本号 7 次
  3. 已开启 USB 调试:设置 > 系统 > 开发者选项 > USB 调试
  4. 通过 USB 数据线连接电脑
  5. 在 DevEco Studio 中配置签名(真机调试必须签名)

连接验证

# 查看已连接设备
hdc list targets

# 预期输出(设备序列号)
1234567890ABCDEF    # 真机设备

签名配置(真机调试必需):

  1. 在 DevEco Studio 中打开 File > Project Structure > Project > Signing Configs
  2. 勾选 Automatically generate signature
  3. 登录华为开发者账号
  4. 选择或创建调试证书和 Profile
  5. 签名信息会自动写入 build-profile.json5signingConfigs 字段
2.3.4 调试方式对比
特性 Local Emulator Remote Simulator 真机调试
响应速度 中(受网络影响)
硬件能力 部分(虚拟相机) 部分 完整
NFC 支持 不支持 不支持 支持
相机支持 虚拟相机 不支持 真实相机
签名要求 不需要 不需要 必须签名
适用场景 UI 开发调试 临时验证 功能完整性验证
IVGuard 适用性 UI 布局调试 不推荐 推荐

2.4 HDC 工具

2.4.1 HDC 概述

HDC(HarmonyOS Device Connector)是 HarmonyOS 的命令行设备管理工具,功能类似 Android 的 ADB(Android Debug Bridge)。它提供了与 HarmonyOS 设备通信的能力,是开发调试中不可或缺的工具。

HDC 的主要功能包括:

  • 设备管理:列出连接的设备、查看设备信息
  • 应用管理:安装、卸载、启动应用
  • 文件操作:在电脑和设备间推送/拉取文件
  • 日志收集:获取设备运行日志(hilog)
  • Shell 命令:在设备上执行 shell 命令
  • 端口转发:设置端口映射,支持网络调试
2.4.2 常用 HDC 命令
# 设备管理
hdc list targets                          # 列出所有连接的设备
hdc -t <deviceId> shell                   # 连接指定设备

# 应用安装与运行
hdc install <hap_path>                    # 安装 HAP 包
hdc install -r <hap_path>                 # 覆盖安装
hdc uninstall <bundleName>                # 卸载应用
hdc shell aa start -a EntryAbility -b com.example.ivguard  # 启动应用



# 日志收集
hdc hilog                                 # 实时查看日志
hdc hilog -T IVGuard                      # 按 tag 过滤日志
hdc hilog -x                              # 清除日志缓冲区

# 文件操作
hdc file send <local_path> <remote_path>  # 推送文件到设备
hdc file recv <remote_path> <local_path>  # 从设备拉取文件

# 端口转发
hdc fport tcp:8080 tcp:8080              # 设置端口转发

# 设备信息
hdc shell param get const.ohos.apiversion  # 查看设备 API 版本
hdc shell param get const.product.devicetype  # 查看设备类型
2.4.3 IVGuard 开发中的 HDC 使用场景

在 IVGuard 的开发调试中,HDC 主要用于以下场景:

  1. 安装调试包:将编译好的 HAP 推送到真机进行测试

    hdc install F:\projects\IVGuard\entry\build\default\outputs\default\entry-default.hap
    
  2. 收集运行日志:监控 IVGuard 运行时的日志输出

    hdc hilog -T IVGuard
    
  3. 调试相机功能:查看相机 API 的调用日志

    hdc hilog | findstr "Camera"
    
  4. 调试 NFC 读取:查看 NFC 标签读取的日志

    hdc hilog | findstr "NFC"
    

3. 项目创建流程

3.1 copy-template.mjs 脚本原理

IVGuard 项目使用 DevEco CLI 提供的 copy-template.mjs 脚本进行项目脚手架生成。该脚本的核心原理是一个"复制 → 替换 → 生成"的三步流程。

3.1.1 模板仓库结构

DevEco CLI 维护了一套标准化的 HarmonyOS 项目模板,模板中的关键文件使用占位符标记需要替换的内容:

template/
├── AppScope/
│   ├── app.json5                        # 包含 {{BUNDLE_NAME}} 占位符
│   └── resources/base/element/
│       └── string.json                  # 包含 {{APP_NAME}} 占位符
├── entry/
│   ├── build-profile.json5
│   ├── src/main/
│   │   ├── module.json5
│   │   ├── ets/
│   │   │   ├── entryability/
│   │   │   │   └── EntryAbility.ets
│   │   │   └── pages/
│   │   │       └── Index.ets
│   │   └── resources/
│   │       └── base/
│   │           ├── profile/
│   │           │   └── main_pages.json
│   │           └── element/
│   │               └── string.json
│   └── oh-package.json5
├── build-profile.json5                  # 包含 {{SDK_VERSION}} 占位符
├── oh-package.json5
├── hvigorfile.ts
└── hvigor/
    └── hvigor-config.json5
3.1.2 三步流程详解

第一步:复制模板(Copy)

脚本首先将模板目录整体复制到目标路径。以 IVGuard 为例:

// 简化的复制逻辑
const templateDir = path.join(__dirname, 'templates', 'empty');
const targetDir = 'F:/projects/IVGuard';
fs.cpSync(templateDir, targetDir, { recursive: true });

这一步生成的是"原始模板",所有占位符尚未替换。

第二步:替换占位符(Replace)

脚本遍历目标目录中的所有文本文件,将占位符替换为用户提供的实际值:

// 简化的替换逻辑
const replacements = {
  '{{BUNDLE_NAME}}': 'com.example.ivguard',
  '{{APP_NAME}}': 'IVGuard',
  '{{SDK_VERSION}}': '6.1.1(24)',
  '{{MODULE_NAME}}': 'entry',
};

function replaceInFile(filePath: string): void {
  let content = fs.readFileSync(filePath, 'utf-8');
  for (const [placeholder, value] of Object.entries(replacements)) {
    content = content.replaceAll(placeholder, value);
  }
  fs.writeFileSync(filePath, content, 'utf-8');
}

第三步:生成项目(Generate)

替换完成后,脚本执行额外的初始化操作:

  • 生成 oh-package-lock.json5:运行 ohpm install 生成依赖锁文件
  • 创建 .gitignore:写入 HarmonyOS 项目的标准忽略规则
  • 验证项目结构:检查关键文件是否存在且内容合法
3.1.3 IVGuard 项目创建的实际调用

IVGuard 项目的创建命令格式如下:

# 通过 DevEco CLI 创建
devecocli create --name IVGuard --bundle com.example.ivguard --api 24

# 或通过 copy-template.mjs 直接调用
node copy-template.mjs --name IVGuard --bundle com.example.ivguard --api 24 --output F:/projects/IVGuard

创建成功后,会在 F:\projects\IVGuard 目录下生成完整的项目结构。

3.2 模板项目结构解析

通过 copy-template.mjs 生成的 IVGuard 基础项目结构如下:

F:\projects\IVGuard\
├── AppScope/                             # 应用级配置与资源
│   ├── app.json5                         # 应用标识配置
│   └── resources/                        # 应用级资源
│       └── base/
│           ├── element/
│           │   └── string.json           # 应用级字符串资源
│           └── media/
│               ├── layered_image.json    # 分层图标配置
│               ├── foreground.png        # 前景图标
│               └── background.png        # 背景图标
├── entry/                                # 主模块(HAP)
│   ├── build-profile.json5               # 模块级构建配置
│   ├── hvigorfile.ts                     # 模块级构建脚本
│   ├── oh-package.json5                  # 模块级依赖配置
│   ├── obfuscation-rules.txt             # 混淆规则
│   ├── src/
│   │   └── main/
│   │       ├── module.json5              # 模块声明配置
│   │       ├── ets/                      # ArkTS 源码
│   │       │   ├── entryability/
│   │       │   │   └── EntryAbility.ets  # 入口 Ability
│   │       │   ├── entrybackupability/
│   │       │   │   └── EntryBackupAbility.ets  # 备份扩展
│   │       │   └── pages/
│   │       │       └── Index.ets         # 首页
│   │       └── resources/                # 模块级资源
│   │           └── base/
│   │               ├── element/
│   │               │   ├── string.json   # 字符串资源
│   │               │   ├── color.json    # 颜色资源
│   │               │   └── float.json    # 尺寸资源
│   │               ├── media/
│   │               │   ├── startIcon.png # 启动图标
│   │               │   ├── layered_image.json
│   │               │   ├── foreground.png
│   │               │   └── background.png
│   │               └── profile/
│   │                   ├── main_pages.json  # 页面路由
│   │                   └── backup_config.json  # 备份配置
│   └── .gitignore
├── build-profile.json5                   # 工程级构建配置
├── hvigorfile.ts                         # 工程级构建脚本
├── oh-package.json5                      # 工程级依赖配置
├── oh-package-lock.json5                 # 依赖锁文件
├── code-linter.json5                     # 代码检查配置
├── hvigor/                               # Hvigor 构建工具配置
│   └── hvigor-config.json5               # 构建工具版本与参数
└── .gitignore                            # Git 忽略规则
3.2.1 目录职责说明

AppScope/:应用级作用域,存放全局性的配置和资源。该目录的内容对所有模块可见:

  • app.json5:定义应用的唯一标识(bundleName)、版本号、图标和名称
  • resources/:应用级资源,如应用图标、应用名称等

entry/:主模块目录,HarmonyOS 中的"入口模块"(entry module),每个应用至少有一个 entry 模块:

  • build-profile.json5:模块级构建配置,定义编译选项、混淆规则等
  • src/main/ets/:ArkTS 源码目录,所有页面、组件、服务代码都在此处
  • src/main/resources/:模块级资源,包括字符串、颜色、图片、配置文件等
  • module.json5:模块声明文件,定义 Ability、权限、页面路由等

hvigor/:Hvigor 构建工具的配置目录,存放构建工具的版本和运行参数。

根目录文件

  • build-profile.json5:工程级构建配置,定义签名、SDK 版本、模块列表等
  • hvigorfile.ts:工程级构建脚本,注册 Hvigor 插件
  • oh-package.json5:工程级依赖声明,类似 npm 的 package.json

3.3 build-profile.json5 配置详解

build-profile.json5 是 HarmonyOS 项目中最核心的构建配置文件,分为工程级和模块级两个层级。

3.3.1 工程级 build-profile.json5

IVGuard 的工程级 build-profile.json5 位于项目根目录,完整内容如下:

{
  "app": {
    "signingConfigs": [],
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)",
        "runtimeOS": "HarmonyOS",
        "buildOption": {
          "strictMode": {
            "caseSensitiveCheck": true,
            "useNormalizedOHMUrl": true
          }
        }
      }
    ],
    "buildModeSet": [
      {
        "name": "debug"
      },
      {
        "name": "release"
      }
    ]
  },
  "modules": [
    {
      "name": "entry",
      "srcPath": "./entry",
      "targets": [
        {
          "name": "default",
          "applyToProducts": [
            "default"
          ]
        }
      ]
    }
  ]
}

字段详解

app.signingConfigs:签名方案配置数组。当前为空数组 [],表示未配置签名,构建时会产生以下警告:

WARN: Will skip sign 'hos_hap'. No signingConfigs profile is configured in current project.

真机调试或应用发布时必须配置签名,典型配置如下:

{
  "signingConfigs": [
    {
      "name": "default",
      "type": "HarmonyOS",
      "material": {
        "certpath": "D:/SigningConfig/debug_hos.cer",
        "storePassword": "加密后的密码",
        "keyAlias": "debugKey",
        "keyPassword": "加密后的密码",
        "profile": "D:/SigningConfig/debug_hos.p7b",
        "signAlg": "SHA256withECDSA",
        "storeFile": "D:/SigningConfig/debug_hos.p12"
      }
    }
  ]
}

各字段含义:

字段 说明
name 签名方案名称,仅支持字母和数字
type 签名类型:HarmonyOSOpenHarmony
material.certpath 证书文件路径(.cer 后缀)
material.storePassword 密钥库密码(密文存储)
material.keyAlias 密钥别名
material.keyPassword 密钥密码(密文存储)
material.profile Profile 文件路径(.p7b 后缀)
material.signAlg 签名算法,当前仅支持 SHA256withECDSA
material.storeFile 密钥库文件路径(.p12 后缀)

app.products:产品品类配置数组,可配置多个 product 以实现多目标构建:

字段 说明
name Product 名称,如 default
signingConfig 关联的签名方案名称
targetSdkVersion 目标 SDK 版本
compatibleSdkVersion 最低兼容 SDK 版本
runtimeOS 运行时操作系统:HarmonyOSOpenHarmony
buildOption.strictMode 严格模式选项
buildOption.strictMode.caseSensitiveCheck 大小写敏感检查
buildOption.strictMode.useNormalizedOHMUrl 使用标准化的 OHM URL

app.buildModeSet:构建模式集合,定义可用的构建模式:

"buildModeSet": [
  { "name": "debug" },    // 调试模式,包含调试信息
  { "name": "release" }   // 发布模式,优化性能和包大小
]

modules:模块配置数组,声明项目中包含的所有模块:

字段 说明
name 模块名称,需与 module.json5 中的 module.name 一致
srcPath 模块源码路径,相对工程根目录
targets 模块 target 列表,用于多目标构建
targets[].name Target 名称,对应模块级 build-profile.json5 中的 targets
targets[].applyToProducts 该 Target 关联的 Product 列表
3.3.2 模块级 build-profile.json5

IVGuard 的模块级 build-profile.json5 位于 entry/ 目录下:

{
  "apiType": "stageMode",
  "buildOption": {
    "resOptions": {
      "copyCodeResource": {
        "enable": false
      }
    }
  },
  "buildOptionSet": [
    {
      "name": "release",
      "arkOptions": {
        "obfuscation": {
          "ruleOptions": {
            "enable": false,
            "files": [
              "./obfuscation-rules.txt"
            ]
          }
        }
      }
    }
  ],
  "targets": [
    {
      "name": "default"
    },
    {
      "name": "ohosTest"
    }
  ]
}

字段详解

字段 说明
apiType API 模型类型:stageMode(Stage 模型)或 faMode(FA 模型,已弃用)
buildOption.resOptions.copyCodeResource.enable 是否复制代码资源,默认 false
buildOptionSet 针对特定构建模式的选项集合
buildOptionSet[].name 构建模式名称,如 release
buildOptionSet[].arkOptions.obfuscation.ruleOptions.enable 是否启用混淆
buildOptionSet[].arkOptions.obfuscation.ruleOptions.files 混淆规则文件列表
targets 模块的 Target 列表
targets[].name Target 名称,default 为默认目标,ohosTest 为测试目标

3.4 hvigor 构建系统原理

3.4.1 Hvigor 概述

Hvigor(Harmony vigor)是 HarmonyOS 生态中基于 TypeScript 实现的全新构建工具,专为提升 ArkTS/JS 开发效率而设计。它替代了早期版本中使用的 Gradle 构建系统,成为 API 9 及以上版本的标准构建工具。

Hvigor 的核心设计理念:

  • 任务编排:通过任务图(Task Graph)管理构建流程的执行顺序和依赖关系
  • 增量构建:通过检测输入输出变化,跳过未修改的任务,显著提升构建速度
  • 插件化:通过插件机制扩展构建能力,如 @ohos/hvigor-ohos-plugin
  • 跨平台:基于 Node.js 实现,可在 Windows、macOS、Linux 上运行
  • CLI 优先:所有构建操作都可通过命令行执行,天然适配 CI/CD
3.4.2 核心概念

Task(任务)

Task 是 Hvigor 构建的基本执行单元,每个 Task 负责一个具体的构建操作。IVGuard 项目构建过程中的关键 Task 包括:

Task 名称 职责 耗时参考
PreBuild 预构建检查,验证项目配置 ~189 ms
CreateModuleInfo 创建模块信息 ~1 ms
GenerateMetadata 生成元数据 ~5 ms
MergeProfile 合并配置文件 ~5 ms
ProcessResource 处理资源文件 ~11 ms
CompileResource 编译资源 ~1030 ms
CompileArkTS 编译 ArkTS 源码 ~8726 ms
PackageHap 打包 HAP ~704 ms
SignHap 签名 HAP ~3 ms
assembleHap 组装 HAP 包 ~1 ms
PackageApp 打包 APP ~594 ms

Task Graph(任务图)

Hvigor 在执行构建时,会自动分析 Task 之间的依赖关系,构建一个有向无环图(DAG),然后按拓扑排序执行。例如:

PreBuild ──→ CreateModuleInfo ──→ GenerateMetadata
                                        │
                                        ▼
                              ProcessResource ──→ CompileResource
                                                       │
                                                       ▼
                                              CompileArkTS ──→ PackageHap ──→ SignHap

Plugin(插件)

Hvigor 通过插件机制提供不同类型的构建能力:

  • appTasks:应用级构建任务(来自 @ohos/hvigor-ohos-plugin),在工程级 hvigorfile.ts 中引用
  • hapTasks:HAP 模块构建任务,在模块级 hvigorfile.ts 中引用
  • 自定义插件:开发者可编写自己的 Hvigor 插件扩展构建流程

IVGuard 的工程级 hvigorfile.ts

import { appTasks } from '@ohos/hvigor-ohos-plugin';

export default {
  system: appTasks,  // 内置应用级构建插件
  plugins: []        // 自定义插件列表(当前为空)
}

IVGuard 的模块级 hvigorfile.ts

import { hapTasks } from '@ohos/hvigor-ohos-plugin';

export default {
  system: hapTasks,  // 内置 HAP 模块构建插件
  plugins: []        // 自定义插件列表(当前为空)
}

Hvigor Wrapper(hvigorw)

hvigorw(Windows 上为 hvigorw.bat)是 Hvigor 的包装脚本,类似于 Gradle 的 gradlew。它的作用是:

  • 自动下载和安装正确版本的 Hvigor
  • 确保所有开发者使用相同的构建工具版本
  • 无需全局安装 Hvigor,项目自包含
3.4.3 hvigor-config.json5 配置

hvigor/hvigor-config.json5 定义了 Hvigor 构建工具的版本和运行参数:

{
  "modelVersion": "6.1.1",
  "dependencies": {},
  "execution": {
    // "analyze": "normal",
    // "daemon": true,
    // "incremental": true,
    // "parallel": true,
    // "typeCheck": false,
    // "optimizationStrategy": "memory"
  },
  "logging": {
    // "level": "info"
  },
  "debugging": {
    // "stacktrace": false
  },
  "nodeOptions": {
    // "maxOldSpaceSize": 8192
    // "exposeGC": true
  }
}

关键配置项详解

配置项 默认值 说明
execution.analyze "normal" 构建分析模式:normal/advanced/ultrafine/false
execution.daemon true 启用守护进程,加速后续构建
execution.incremental true 启用增量编译,仅编译变更部分
execution.parallel true 启用并行编译,利用多核 CPU
execution.typeCheck false 启用类型检查,更严格的编译检查
nodeOptions.maxOldSpaceSize 8192 Node.js 守护进程的最大堆内存
3.4.4 构建缓存机制

Hvigor 的缓存机制是其增量构建能力的核心。缓存信息存储在 .hvigor/cache/ 目录下:

.hvigor/cache/
├── meta.json              # 缓存元信息
├── last-build-info.json   # 上次构建信息
├── file-cache.json        # 文件缓存(记录文件哈希)
├── project-config.json    # 项目配置缓存
└── task-cache.json        # Task 执行缓存

当执行增量构建时,Hvigor 会:

  1. 读取 last-build-info.json 获取上次构建的 Task 列表
  2. 对比当前源文件与 file-cache.json 中的哈希值
  3. 对于未变化的文件,标记对应的 Task 为 UP-TO-DATE
  4. 仅执行有变更的 Task 及其下游依赖 Task

从 IVGuard 的构建日志中可以观察到 UP-TO-DATE 标记:

> hvigor UP-TO-DATE :entry:default@PreBuild...
> hvigor UP-TO-DATE :entry:default@GenerateMetadata...
> hvigor UP-TO-DATE :entry:default@MergeProfile...
> hvigor UP-TO-DATE :entry:default@ProcessResource...
> hvigor UP-TO-DATE :entry:default@CompileResource...
> hvigor Finished :entry:default@CompileArkTS... after 8 s 726 ms

可以看到,大部分 Task 被标记为 UP-TO-DATE 跳过了,只有 CompileArkTS 因为源码变更而重新执行,整个构建仅耗时 16 秒(全量构建通常需要 30 秒以上)。


4. 项目配置文件深度解析

4.1 module.json5

module.json5 是 HarmonyOS 模块的核心声明文件,定义了模块的类型、Ability 配置、权限声明等关键信息。IVGuard 的 module.json5 位于 entry/src/main/module.json5

4.1.1 模块基础配置
{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages"
  }
}
字段 说明
name "entry" 模块名称,需与 build-profile.json5 中 modules.name 一致
type "entry" 模块类型:entry(入口模块)、feature(特性模块)、har(静态共享库)、hsp(动态共享库)
description "$string:module_desc" 模块描述,引用字符串资源,值为"IVGuard 静脉输液智能监控系统"
mainElement "EntryAbility" 模块的主入口 Ability 名称
deviceTypes ["phone"] 支持的设备类型:phonetablettvwearablecar
deliveryWithInstall true 是否随应用安装一起交付
installationFree false 是否支持免安装(原子化服务)
pages "$profile:main_pages" 页面路由配置,引用 profile 资源文件
4.1.2 EntryAbility 配置详解

Ability 是 HarmonyOS 应用的基本功能单元,类似 Android 的 Activity。IVGuard 的 EntryAbility 配置:

{
  "name": "EntryAbility",
  "srcEntry": "./ets/entryability/EntryAbility.ets",
  "description": "$string:EntryAbility_desc",
  "icon": "$media:layered_image",
  "label": "$string:EntryAbility_label",
  "startWindowIcon": "$media:startIcon",
  "startWindowBackground": "$color:start_window_background",
  "exported": true,
  "skills": [
    {
      "entities": [
        "entity.system.home"
      ],
      "actions": [
        "ohos.want.action.home"
      ]
    }
  ]
}

各字段详解

  • name:Ability 的类名,必须与源码中的 export default class EntryAbility 一致
  • srcEntry:Ability 的源码入口路径,相对于 ets/ 目录
  • icon:Ability 图标,引用 media 资源
  • label:Ability 名称,引用 string 资源,值为"IVGuard"
  • startWindowIcon:启动窗口图标(应用启动到首帧渲染期间显示的图标)
  • startWindowBackground:启动窗口背景色,引用 color 资源,值为 #FFFFFF
  • exportedtrue 表示该 Ability 可被其他应用调用。对于入口 Ability,必须设为 true,否则应用无法从桌面启动
  • skills:定义 Ability 可以响应的 Intent 类型

skills 配置详解

skills 类似 Android 的 Intent Filter,定义了 Ability 可以接收的隐式 Intent:

  • entitiesentity.system.home 表示该 Ability 是应用的入口,可以从桌面图标启动
  • actionsohos.want.action.home 是系统定义的启动首页 Action

这个 skills 配置使得 IVGuard 的图标可以出现在设备桌面上,点击图标即可启动应用。

对应的源码实现(EntryAbility.ets):

import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.context.getApplicationContext().setColorMode(
      ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET
    );
    hilog.info(0x0000, 'testTag', 'Ability onCreate');
  }

  onDestroy(): void {
    hilog.info(0x0000, 'testTag', 'Ability onDestroy');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    hilog.info(0x0000, 'testTag', 'Ability onWindowStageCreate');
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        hilog.error(0x0000, 'testTag', 'Failed to load content');
        return;
      }
      hilog.info(0x0000, 'testTag', 'Succeeded in loading content.');
    });
  }

  onWindowStageDestroy(): void {
    hilog.info(0x0000, 'testTag', 'Ability onWindowStageDestroy');
  }

  onForeground(): void {
    hilog.info(0x0000, 'testTag', 'Ability onForeground');
  }

  onBackground(): void {
    hilog.info(0x0000, 'testTag', 'Ability onBackground');
  }
}

UIAbility 的生命周期回调包括:onCreateonWindowStageCreateonForegroundonBackgroundonWindowStageDestroyonDestroy。在 onWindowStageCreate 中,通过 windowStage.loadContent('pages/Index') 加载了 Index 首页。

4.1.3 ExtensionAbility(backup)配置

IVGuard 配置了一个 backup 类型的 ExtensionAbility:

{
  "name": "EntryBackupAbility",
  "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
  "type": "backup",
  "exported": false,
  "metadata": [
    {
      "name": "ohos.extension.backup",
      "resource": "$profile:backup_config"
    }
  ]
}

各字段详解

  • typebackup 表示这是一个备份扩展能力
  • exportedfalse 表示该 ExtensionAbility 不对外暴露
  • metadata:声明元数据配置
    • nameohos.extension.backup 是备份扩展的固定标识
    • resource:指向备份配置文件 backup_config.json

对应的源码实现:

import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

export default class EntryBackupAbility extends BackupExtensionAbility {
  async onBackup() {
    hilog.info(0x0000, 'testTag', 'onBackup ok');
    await Promise.resolve();
  }

  async onRestore(bundleVersion: BundleVersion) {
    hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion));
    await Promise.resolve();
  }
}

备份配置文件 backup_config.json

{
  "allowToBackupRestore": true
}

这表示允许 IVGuard 应用参与系统的备份恢复流程,用户在更换设备或重装应用后可以恢复数据。当前配置是基础版本,仅允许备份恢复,未指定具体的 includes/excludes 路径。如需更精细的控制(例如排除临时缓存数据),可以扩展配置:

{
  "allowToBackupRestore": true,
  "includes": [
    "/data/storage/el2/base/files/",
    "/data/storage/el2/base/preferences/"
  ],
  "excludes": [
    "/data/storage/el2/base/files/cache/"
  ]
}
4.1.4 8 项 requestPermissions 权限声明

IVGuard 声明了 8 项权限,每项权限都与应用的特定功能紧密关联:

1. ohos.permission.CAMERA(相机权限)

{
  "name": "ohos.permission.CAMERA",
  "reason": "$string:camera_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "inuse"
  }
}

用途:IVGuard 的核心功能是通过手机相机实时监控输液瓶的液位变化。VisionService 使用相机 API 捕获输液瓶图像,通过图像分析算法检测液位高度。

授权类型user_grant(用户授权),安装后首次使用时系统会弹出授权弹窗。

2. ohos.permission.NFC_TAG(NFC 标签权限)

{
  "name": "ohos.permission.NFC_TAG",
  "reason": "$string:nfc_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "inuse"
  }
}

用途:护士可以通过手机触碰输液瓶上的 NFC 贴纸,快速读取药物信息(药品名称、剂量、配伍禁忌等),避免手动输入的错误。

授权类型user_grant

3. ohos.permission.NOTIFICATION_CONTROLLER(通知控制权限)

{
  "name": "ohos.permission.NOTIFICATION_CONTROLLER",
  "reason": "$string:notify_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "inuse"
  }
}

用途:当输液即将结束时,IVGuard 需要向护士和患者家属发送提醒通知。NotificationService 负责管理通知的发送。

授权类型user_grant

4. ohos.permission.KEEP_BACKGROUND_RUNNING(后台保活权限)

{
  "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
  "reason": "$string:bg_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "always"
  }
}

用途:输液过程通常持续数小时,IVGuard 需要在后台持续监控输液进度,不能因为应用退到后台就停止监控。这是唯一一个 when: "always" 的权限。

授权类型user_grant

5. ohos.permission.VIBRATE(振动权限)

{
  "name": "ohos.permission.VIBRATE"
}

用途:当输液异常或即将结束时,除了通知提醒外,还需要通过手机振动提供触觉反馈。

授权类型system_grant(系统授权),安装时自动授予,无需用户确认。因此不需要 reasonusedScene 字段。

6. ohos.permission.LOCATION(精确定位权限)

{
  "name": "ohos.permission.LOCATION",
  "reason": "$string:location_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "inuse"
  }
}

用途:IVGuard 的 HospitalNavPage 提供医院室内导航功能,帮助家属找到输液室位置,需要精确位置信息。

授权类型user_grant

7. ohos.permission.APPROXIMATELY_LOCATION(粗略定位权限)

{
  "name": "ohos.permission.APPROXIMATELY_LOCATION",
  "reason": "$string:location_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "inuse"
  }
}

用途:作为 LOCATION 权限的补充,粗略定位可以在用户拒绝精确定位时提供基本的定位能力。

注意:在 HarmonyOS 中,申请 LOCATION 权限时通常需要同时申请 APPROXIMATELY_LOCATION

8. ohos.permission.GET_WIFI_INFO(WiFi 信息权限)

{
  "name": "ohos.permission.GET_WIFI_INFO"
}

用途:获取 WiFi 连接信息,可用于辅助室内定位(WiFi 指纹定位)或判断设备是否在医院网络环境中。

授权类型system_grant,安装时自动授予。

4.1.5 权限分类总结
权限 授权类型 使用时机 关联功能
CAMERA user_grant inuse 液位视觉监控
NFC_TAG user_grant inuse NFC 药物信息读取
NOTIFICATION_CONTROLLER user_grant inuse 输液结束提醒
KEEP_BACKGROUND_RUNNING user_grant always 后台持续监控
VIBRATE system_grant - 振动提醒
LOCATION user_grant inuse 医院导航
APPROXIMATELY_LOCATION user_grant inuse 辅助定位
GET_WIFI_INFO system_grant - WiFi 辅助定位

user_grant 与 system_grant 的区别

  • system_grant:系统授权权限,应用安装时自动授予,用户无需确认。如 VIBRATE、GET_WIFI_INFO。配置时 reasonusedScene 为选填字段。
  • user_grant:用户授权权限,需要用户在弹窗中手动授权。如 CAMERA、LOCATION、NFC_TAG。配置时 reasonusedScene必填字段,否则应用上架可能被驳回。

reason 字符串的重要性

对于 user_grant 权限,reason 的内容会在系统授权弹窗中展示给用户。IVGuard 的 reason 字符串都在 string.json 中定义:

{
  "name": "camera_reason",
  "value": "需要使用相机监控输液瓶液位变化"
}

reason 的内容应直白、具体,建议句式为"用于做某事",建议长度小于 72 个字符(约 36 个汉字)。

4.2 main_pages.json

main_pages.json 定义了模块中的页面路由注册表。只有在此文件中注册的页面,才能通过路由进行导航。

IVGuard 的页面路由配置:

{
  "src": [
    "pages/Index",
    "pages/HomePage",
    "pages/MonitorPage",
    "pages/MedicinePage",
    "pages/MedicineDetailPage",
    "pages/HistoryPage",
    "pages/AnalysisPage",
    "pages/CostPage",
    "pages/CostDetailPage",
    "pages/InsuranceSetupPage",
    "pages/SettingsPage",
    "pages/HospitalNavPage",
    "pages/NurseHomePage",
    "pages/PatientListPage",
    "pages/NurseAnalysisPage",
    "pages/FamilyHomePage",
    "pages/FamilyAlertPage"
  ]
}

共注册了 17 个页面,按功能模块分类如下:

功能模块 页面 说明
通用 Index 启动页/角色选择
患者端 HomePage 患者首页(监控概览)
患者端 MonitorPage 实时监控页面
患者端 MedicinePage 药物列表页
患者端 MedicineDetailPage 药物详情页
患者端 HistoryPage 历史记录页
患者端 AnalysisPage 分析报告页
患者端 CostPage 费用概览页
患者端 CostDetailPage 费用详情页
患者端 InsuranceSetupPage 医保配置页
通用 SettingsPage 设置页
通用 HospitalNavPage 医院导航页
护士端 NurseHomePage 护士工作台
护士端 PatientListPage 患者列表页
护士端 NurseAnalysisPage 护士聚合分析页
家属端 FamilyHomePage 家属远程监控页
家属端 FamilyAlertPage 家属预警记录页

重要规则

  1. 页面路径格式为 pages/PageName,不需要写文件扩展名(.ets
  2. 所有页面文件必须位于 entry/src/main/ets/pages/ 目录下
  3. 每个页面文件必须有一个被 @Entry 装饰器标记的 struct
  4. 未在 main_pages.json 中注册的页面无法通过路由访问
  5. pages 数组中的第一个元素(Index)是应用的默认首页

页面路由导航

在 ArkTS 代码中,页面导航通过 router 模块实现:

import { router } from '@kit.ArkUI';

// 跳转到指定页面
router.pushUrl({ url: 'pages/MonitorPage' });

// 替换当前页面(不可返回)
router.replaceUrl({ url: 'pages/HomePage' });

// 带参数跳转
router.pushUrl({
  url: 'pages/MedicineDetailPage',
  params: { medicineId: 'med_001' }
});

// 获取路由参数
const params = router.getParams() as Record<string, Object>;

4.3 string.json

string.json 是 HarmonyOS 的字符串资源文件,用于实现多语言支持和资源集中管理。IVGuard 项目有两级 string.json:

应用级AppScope/resources/base/element/string.json):

{
  "string": [
    {
      "name": "app_name",
      "value": "IVGuard"
    }
  ]
}

模块级entry/src/main/resources/base/element/string.json)包含 22 个字符串资源,主要分为以下类别:

模块描述与 Ability 标识

name value 引用位置
module_desc IVGuard 静脉输液智能监控系统 module.json5 → description
EntryAbility_desc 静脉输液智能监控与提醒 module.json5 → abilities[].description
EntryAbility_label IVGuard module.json5 → abilities[].label

权限说明字符串(reason):

name value 对应权限
camera_reason 需要使用相机监控输液瓶液位变化 CAMERA
nfc_reason 需要读取NFC贴纸中的药物信息 NFC_TAG
notify_reason 需要在输液快结束时发送提醒通知 NOTIFICATION_CONTROLLER
bg_reason 需要在后台持续监控输液进度 KEEP_BACKGROUND_RUNNING
location_reason 需要获取位置信息用于医院室内导航 LOCATION / APPROXIMATELY_LOCATION

页面标题字符串

name value
page_home 监控
page_monitor 实时监控
page_medicine 药物
page_history 历史
page_analysis 分析
page_cost 费用
page_settings 设置
page_hospital_nav 医院导航
page_nurse_home 护士工作台
page_patient_list 患者列表
page_nurse_analysis 聚合分析
page_family_home 远程监控
page_family_alert 预警记录

免责声明

name value
disclaimer 本应用仅为辅助提醒工具,不替代专业护理。费用估算仅供参考,以医院实际收费为准。

资源引用方式

在配置文件中:$string:resource_name(如 $string:camera_reason

在 ArkTS 代码中:$r('app.string.resource_name')$r('entry.string.resource_name')

4.4 oh-package.json5

oh-package.json5 是 OpenHarmony 包管理器(ohpm)的配置文件,类似 npm 的 package.json。IVGuard 有两级配置:

工程级oh-package.json5):

{
  "modelVersion": "6.1.1",
  "description": "Please describe the basic information.",
  "dependencies": {},
  "devDependencies": {
    "@ohos/hypium": "1.0.25",
    "@ohos/hamock": "1.0.0"
  }
}
  • modelVersion:SDK 模型版本号
  • dependencies:运行时依赖(当前为空,IVGuard 没有使用第三方运行时库)
  • devDependencies:开发依赖
    • @ohos/hypium:HarmonyOS 单元测试框架
    • @ohos/hamock:HarmonyOS Mock 框架

模块级entry/oh-package.json5):

{
  "name": "entry",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "",
  "author": "",
  "license": "",
  "dependencies": {}
}

依赖锁文件oh-package-lock.json5)记录了依赖的精确版本、完整性校验和下载地址:

{
  "meta": {
    "stableOrder": true,
    "enableUnifiedLockfile": false
  },
  "lockfileVersion": 3,
  "specifiers": {
    "@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0",
    "@ohos/hypium@1.0.25": "@ohos/hypium@1.0.25"
  },
  "packages": {
    "@ohos/hamock@1.0.0": {
      "name": "@ohos/hamock",
      "version": "1.0.0",
      "integrity": "sha512-K6lDPYc6VkKe6...",
      "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hamock/-/hamock-1.0.0.har",
      "registryType": "ohpm"
    },
    "@ohos/hypium@1.0.25": {
      "name": "@ohos/hypium",
      "version": "1.0.25",
      "integrity": "sha512-l6uO2pjl8HyE...",
      "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.25.har",
      "registryType": "ohpm"
    }
  }
}

锁文件确保所有开发者使用完全一致的依赖版本。切勿手动编辑此文件,应通过 ohpm install 命令自动生成。

添加第三方依赖的流程

# 安装依赖
ohpm install @ohos/lottie  # 示例:安装 Lottie 动画库

# 这会自动更新 oh-package.json5 和 oh-package-lock.json5

4.5 app.json5

app.json5 是应用级标识配置文件,位于 AppScope/app.json5

{
  "app": {
    "bundleName": "com.example.ivguard",
    "vendor": "example",
    "versionCode": 1000000,
    "versionName": "1.0.0",
    "icon": "$media:layered_image",
    "label": "$string:app_name"
  }
}

字段详解

字段 说明
bundleName com.example.ivguard 应用唯一标识包名,全局唯一,一旦发布不可更改
vendor example 应用开发商名称
versionCode 1000000 版本号(整数),每次发布必须递增。格式通常为 AABBCC,AA 为主版本,BB 为次版本,CC 为修订号
versionName "1.0.0" 版本名称(字符串),面向用户的可见版本号
icon $media:layered_image 应用图标,引用 media 资源
label $string:app_name 应用名称,引用 string 资源,值为"IVGuard"

bundleName 的命名规范

  • 采用反域名格式:com.公司名.产品名
  • IVGuard 使用 com.example.ivguard,其中 example 是开发阶段的占位符
  • 正式发布前应替换为真实公司域名,如 com.yourcompany.ivguard
  • bundleName 一旦在 AppGallery 上发布,就不可更改,务必在发布前确认

4.6 其他配置文件

4.6.1 color.json

颜色资源文件,定义应用中使用的颜色:

{
  "color": [
    {
      "name": "start_window_background",
      "value": "#FFFFFF"
    }
  ]
}

目前只定义了启动窗口背景色(白色)。后续可以扩展更多品牌色、主题色等。

4.6.2 float.json

尺寸资源文件,定义应用中使用的浮点数尺寸:

{
  "float": [
    {
      "name": "page_text_font_size",
      "value": "50fp"
    }
  ]
}

使用 fp(font-independent pixel)单位,确保在不同屏幕密度的设备上显示效果一致。

4.6.3 code-linter.json5

代码检查配置文件,定义了 ArkTS 代码的静态分析规则:

{
  "files": [
    "**/*.ets"
  ],
  "ignore": [
    "**/src/ohosTest/**/*",
    "**/src/test/**/*",
    "**/src/mock/**/*",
    "**/node_modules/**/*",
    "**/oh_modules/**/*",
    "**/build/**/*",
    "**/.preview/**/*"
  ],
  "ruleSet": [
    "plugin:@performance/recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "rules": {
    "@security/no-unsafe-aes": "error",
    "@security/no-unsafe-hash": "error",
    "@security/no-unsafe-mac": "warn",
    "@security/no-unsafe-dh": "error",
    "@security/no-unsafe-dsa": "error",
    "@security/no-unsafe-ecdsa": "error",
    "@security/no-unsafe-rsa-encrypt": "error",
    "@security/no-unsafe-rsa-sign": "error",
    "@security/no-unsafe-rsa-key": "error",
    "@security/no-unsafe-dsa-key": "error",
    "@security/no-unsafe-dh-key": "error",
    "@security/no-unsafe-3des": "error"
  }
}

关键配置

  • ruleSet:启用了性能推荐规则和 TypeScript ESLint 推荐规则
  • security 规则:全面禁止不安全的加密算法,包括不安全的 AES、Hash、MAC、DH、DSA、ECDSA、RSA 以及 3DES。这对 IVGuard 这样涉及医疗数据的应用尤为重要——必须使用安全的加密算法保护患者隐私数据。
4.6.4 obfuscation-rules.txt

代码混淆规则文件,定义了 release 构建时的混淆策略:

-enable-property-obfuscation
-enable-toplevel-obfuscation
-enable-filename-obfuscation
-enable-export-obfuscation

当前启用了四种混淆:属性名混淆、顶层名称混淆、文件名混淆、导出名称混淆。但在模块级 build-profile.json5 中,混淆的 enable 设为 false,意味着当前 release 构建实际上未启用混淆。如果需要启用,将 ruleOptions.enable 改为 true 即可。


5. 构建流程详解

5.1 构建流水线概览

HarmonyOS 应用的构建过程是一个多阶段的流水线,从源码到最终可安装的安装包,需要经过以下核心阶段:

PreBuild → Compile → Package → Sign
5.1.1 PreBuild 阶段

PreBuild 阶段负责构建前的准备工作,包括:

工程级预构建

  • PreBuildApp:工程级预构建检查(耗时约 1 ms)
  • DuplicateDependencyCheck:重复依赖检查,确保没有依赖冲突

模块级预构建

  • PreBuild:模块级预构建检查(耗时约 189 ms),验证项目配置的合法性
  • CreateModuleInfo:创建模块信息对象(耗时约 1 ms)
  • GenerateMetadata:生成模块元数据(耗时约 5 ms)
  • ConfigureCmake:配置 CMake(如果包含 Native 代码)
  • MergeProfile:合并各配置文件(耗时约 5 ms)
  • CreateBuildProfile:创建构建配置(耗时约 5 ms)
  • PreCheckSyscap:预检查系统能力(SysCap)
  • GeneratePkgContextInfo:生成包上下文信息(耗时约 11 ms)
  • GeneratePkgSdkInfo:生成包 SDK 信息(耗时约 6 ms)
  • ProcessIntegratedHsp:处理集成的 HSP 模块
5.1.2 Compile 阶段

Compile 阶段是构建的核心,负责将源码编译为可执行的中间产物:

资源处理

  • ProcessProfile:处理 profile 配置文件(耗时约 661 ms)
  • ProcessRouterMap:处理路由映射(耗时约 5 ms)
  • ProcessShareConfig:处理共享配置(耗时约 4 ms)
  • ProcessStartupConfig:处理启动配置(耗时约 3 ms)
  • ProcessResource:处理资源文件,编译图片、字符串等(耗时约 11 ms)
  • CompileResource:编译资源为二进制格式(耗时约 1030 ms)

代码编译

  • BuildJS:构建 JavaScript/ArkTS 基础环境(耗时约 4 ms)
  • CompileArkTS核心编译步骤,将 ArkTS 源码编译为字节码(耗时约 8726 ms,占总构建时间的大部分)

Native 编译(如果包含 C/C++ 代码):

  • BuildNativeWithCmake:使用 CMake 构建 Native 代码
  • BuildNativeWithNinja:使用 Ninja 编译 Native 代码
  • DoNativeStrip:去除 Native 库的调试符号
  • CacheNativeLibs:缓存 Native 库

其他编译步骤

  • ProcessLibs:处理依赖库文件(耗时约 8 ms)
  • GenerateLoaderJson:生成加载器 JSON(耗时约 11 ms)
5.1.3 Package 阶段

Package 阶段将编译产物打包为 HAP 包:

  • MakePackInfo:生成打包信息(耗时约 5 ms)
  • SyscapTransform:系统能力转换(耗时约 2 ms)
  • GeneratePkgModuleJson:生成模块 JSON(耗时约 6 ms)
  • ProcessCompiledResources:处理已编译的资源(耗时约 2 ms)
  • PackageHap打包 HAP,将所有编译产物打包为 .hap 文件(耗时约 704 ms)
  • PackingCheck:打包检查(耗时约 9 ms)
5.1.4 Sign 阶段

Sign 阶段对 HAP 包进行数字签名:

  • SignHap:使用签名证书对 HAP 包签名(耗时约 3 ms)

工程级打包

  • MakeProjectPackInfo:生成工程打包信息(耗时约 5 ms)
  • ProcessProjectPrivacyProfile:处理隐私声明
  • GeneratePackRes:生成打包资源(耗时约 2 ms)
  • PackageApp打包 APP,将所有 HAP 合并为 .app 文件(耗时约 594 ms)
  • SignApp:对 APP 包签名(耗时约 1 ms)
  • assembleApp:最终组装 APP 包
5.1.5 IVGuard 构建日志分析

以下是从 IVGuard 实际构建日志中提取的关键时间线(增量构建):

> hvigor Finished ::PreBuildApp... after 1 ms
> hvigor UP-TO-DATE :entry:default@PreBuild...
> hvigor Finished ::DuplicateDependencyCheck... after 1 ms
> hvigor Finished :entry:default@CreateModuleInfo... after 2 ms
> hvigor UP-TO-DATE :entry:default@GenerateMetadata...
> hvigor Finished :entry:default@ConfigureCmake... after 1 ms
> hvigor UP-TO-DATE :entry:default@MergeProfile...
> hvigor UP-TO-DATE :entry:default@CreateBuildProfile...
> hvigor Finished :entry:default@PreCheckSyscap... after 1 ms
> hvigor UP-TO-DATE :entry:default@GeneratePkgContextInfo... after 11 ms
> hvigor Finished :entry:default@GeneratePkgSdkInfo... after 5 ms
> hvigor Finished :entry:default@ProcessIntegratedHsp... after 2 ms
> hvigor Finished :entry:default@BuildNativeWithCmake... after 1 ms
> hvigor UP-TO-DATE :entry:default@MakePackInfo...
> hvigor Finished :entry:default@SyscapTransform... after 17 ms
> hvigor UP-TO-DATE :entry:default@ProcessProfile...
> hvigor UP-TO-DATE :entry:default@ProcessRouterMap...
> hvigor UP-TO-DATE :entry:default@ProcessShareConfig...
> hvigor Finished :entry:default@ProcessStartupConfig... after 3 ms
> hvigor Finished :entry:default@BuildNativeWithNinja... after 1 ms
> hvigor UP-TO-DATE :entry:default@ProcessResource...
> hvigor UP-TO-DATE :entry:default@GenerateLoaderJson...
> hvigor UP-TO-DATE :entry:default@ProcessLibs...
> hvigor UP-TO-DATE :entry:default@CompileResource...
> hvigor UP-TO-DATE :entry:default@DoNativeStrip...
> hvigor Finished :entry:default@BuildJS... after 4 ms
> hvigor UP-TO-DATE :entry:default@CacheNativeLibs...
> hvigor Finished :entry:default@CompileArkTS... after 8 s 726 ms
> hvigor Finished :entry:default@GeneratePkgModuleJson... after 6 ms
> hvigor Finished :entry:default@ProcessCompiledResources... after 2 ms
> hvigor Finished :entry:default@PackageHap... after 704 ms
> hvigor Finished :entry:default@PackingCheck... after 9 ms
> hvigor Finished :entry:default@SignHap... after 3 ms
> hvigor Finished :entry:default@CollectDebugSymbol... after 1 ms
> hvigor Finished :entry:default@assembleHap... after 1 ms
> hvigor Finished ::MakeProjectPackInfo... after 5 ms
> hvigor Finished ::ProcessProjectPrivacyProfile... after 6 ms
> hvigor Finished ::GeneratePackRes... after 2 ms
> hvigor Finished ::PackageApp... after 594 ms
> hvigor Finished ::SignApp... after 1 ms
> hvigor Finished ::assembleApp... after 1 ms
> hvigor BUILD SUCCESSFUL in 16 s 142 ms

关键观察

  1. 增量构建效率:大部分 Task 被标记为 UP-TO-DATE,只有 CompileArkTS 重新执行
  2. CompileArkTS 是瓶颈:耗时 8.7 秒,占总构建时间的 54%
  3. PackageHap 次之:耗时 704 ms,占总构建时间的 4.4%
  4. 全量构建约 19-30 秒,增量构建约 16 秒

5.2 ArkTS 编译器的前端检查

ArkTS 编译器在 CompileArkTS 阶段执行严格的前端检查,这是 ArkTS 与普通 TypeScript 的关键区别。

5.2.1 arkts_check 静态分析

arkts_check 是 ArkTS 编译器内置的静态分析工具,在编译前对代码进行规则检查。它会检测以下违规行为:

arkts-no-standalone-this:禁止在 @Component 装饰的 struct 之外使用 this

arkts-no-any-unknown:禁止使用 anyunknown 类型,强制显式类型声明。

arkts-no-obj-literals-as-types:禁止使用对象字面量作为类型,必须使用 class。

arkts-no-destructuring:限制解构赋值的使用场景。

arkts-limited-stdlib:限制标准库的使用,部分 JavaScript 标准库 API 在 ArkTS 中不可用。

这些检查在 CompileArkTS 阶段自动执行,如果检测到违规,会直接导致编译失败并输出错误信息。

5.2.2 IVGuard 中的编译错误示例

在 IVGuard 的首次构建中,遇到了以下编译错误:

ERROR: 10905209 ArkTS Compiler Error
Error Message: Only UI component syntax can be written here.
At File: F:/projects/IVGuard/entry/src/main/ets/pages/CostPage.ets:164:23

ERROR: 10905209 ArkTS Compiler Error
Error Message: Only UI component syntax can be written here.
At File: F:/projects/IVGuard/entry/src/main/ets/pages/CostPage.ets:249:15

ERROR: 10905209 ArkTS Compiler Error
Error Message: Only UI component syntax can be written here.
At File: F:/projects/IVGuard/entry/src/main/ets/component/MonitorOverlay.ets:31:9

ERROR: 10905209 ArkTS Compiler Error
Error Message: Only UI component syntax can be written here.
At File: F:/projects/IVGuard/entry/src/main/ets/component/MonitorOverlay.ets:32:9

Only UI component syntax can be written here 错误是 ArkTS 严格模式中最常见的编译错误之一,表示在 build() 方法中使用了不允许的语法。在 build() 方法中,只能使用 UI 组件声明语法,不能使用普通的 JavaScript 语句(如 const 声明、if/else 块等普通控制流需要改为条件表达式)。

5.3 HAP 打包与签名

5.3.1 HAP 文件结构

HAP(HarmonyOS Ability Package)是 HarmonyOS 应用的基本安装单元,类似 Android 的 APK。一个 HAP 文件实际上是一个 ZIP 压缩包,内部结构如下:

entry-default.hap (ZIP 格式)
├── entry/                    # 模块根目录
│   ├── ets/                  # 编译后的 ArkTS 字节码
│   │   ├── entryability/     # Ability 字节码
│   │   ├── pages/            # 页面字节码
│   │   ├── service/          # 服务字节码
│   │   ├── model/            # 数据模型字节码
│   │   └── component/        # 组件字节码
│   ├── resources/            # 编译后的资源文件
│   │   ├── base/
│   │   └── dark/
│   └── module.json           # 模块配置(编译后)
├── libs/                     # Native 库(.so 文件)
├── resources.index           # 资源索引文件
├── module.json               # 模块清单
└── pack.info                 # 包信息

IVGuard 构建产物的实际路径:

# HAP 包(模块级)
entry/build/default/outputs/default/entry-default-unsigned.hap   # 未签名
entry/build/default/outputs/default/app/entry-default.hap        # 已签名

# APP 包(工程级)
build/outputs/default/IVGuard-default-unsigned.app               # 未签名
5.3.2 APP 与 HAP 的关系

APP(Application Package)是包含一个或多个 HAP 的发布包:

  • APP 包:用于应用市场分发和真机安装,包含所有模块的 HAP
  • HAP 包:单个模块的安装包,可用于模拟器安装和调试

当项目只有一个 entry 模块时,APP 包和 HAP 包的内容基本一致,但文件格式不同(APP 包会额外包含 pack.info 等元数据)。

5.3.3 签名流程

签名是将数字证书附加到 HAP/APP 包的过程,确保应用的完整性和来源可信。

调试签名

  • 通过 DevEco Studio 自动生成(Automatically generate signature)
  • 仅用于开发调试阶段
  • 签名信息存储在 build-profile.json5signingConfigs

发布签名

  • 需要向华为申请发布证书和 Profile
  • 通过 AppGallery Connect 管理签名材料
  • 用于正式上架到华为应用市场

IVGuard 当前的签名状态

由于未配置 signingConfigs,构建时产生以下警告:

WARN: Will skip sign 'hos_hap'. No signingConfigs profile is configured in current project.
WARN: Will skip sign 'app'. No signingConfigs profile is configured in current project.

这意味着生成的 HAP/APP 包是未签名的(-unsigned 后缀),只能在模拟器上安装,无法在真机上安装。真机调试或上架前必须配置签名。

5.4 增量构建 vs 全量构建

5.4.1 增量构建

增量构建是 Hvigor 的核心优化能力。当源码只有部分变更时,增量构建只重新编译受影响的部分,跳过未变化的 Task。

增量构建的判定逻辑

  1. Hvigor 检查每个 Task 的输入文件和输出文件
  2. 计算输入文件的哈希值,与缓存中的哈希值比较
  3. 如果哈希值相同,标记该 Task 为 UP-TO-DATE,跳过执行
  4. 如果哈希值不同,执行该 Task 并更新缓存

IVGuard 增量构建示例(仅修改了 ArkTS 源码):

> hvigor UP-TO-DATE :entry:default@PreBuild...
> hvigor UP-TO-DATE :entry:default@GenerateMetadata...
> hvigor UP-TO-DATE :entry:default@MergeProfile...
> hvigor UP-TO-DATE :entry:default@ProcessResource...
> hvigor UP-TO-DATE :entry:default@CompileResource...
> hvigor Finished :entry:default@CompileArkTS... after 8 s 726 ms
> hvigor Finished :entry:default@PackageHap... after 704 ms
> hvigor BUILD SUCCESSFUL in 16 s 142 ms

大部分 Task 被跳过,只有 CompileArkTS 和后续的打包步骤重新执行。

5.4.2 全量构建

全量构建清除所有缓存,从零开始重新编译所有内容。

触发全量构建的场景

  • 手动执行 hvigorw clean 后再构建
  • 修改了 build-profile.json5 等配置文件
  • 修改了 module.json5 等模块配置
  • 增减了模块或依赖
  • 缓存损坏导致增量构建失败

IVGuard 全量构建示例

> hvigor Finished ::PreBuildApp... after 1 ms
> hvigor Finished :entry:default@PreBuild... after 189 ms
> hvigor Finished :entry:default@CreateModuleInfo... after 1 ms
> ... (所有 Task 都重新执行)
> hvigor Finished :entry:default@CompileArkTS... after 8 s 726 ms
> hvigor Finished :entry:default@PackageHap... after 704 ms
> hvigor BUILD FAILED in 19 s 103 ms

全量构建通常耗时 19-30 秒,比增量构建慢约 20-80%。

5.4.3 何时使用 clean 构建
  • 怀疑缓存污染:多次增量构建仍然失败时,尝试 clean 构建
  • 配置变更后:修改了 build-profile.json5module.json5 等配置文件
  • 依赖变更后:添加或删除了 ohpm 依赖
  • 切换分支后:从 Git 切换到另一个分支,缓存可能失效
# 执行 clean 构建
hvigorw clean assembleHap --no-daemon

6. IVGuard 项目构建实践

6.1 项目概览

IVGuard 项目的关键信息:

属性
项目路径 F:\projects\IVGuard
bundleName com.example.ivguard
targetSdkVersion 6.1.1(24)
compatibleSdkVersion 6.1.1(24)
runtimeOS HarmonyOS
主模块 entry(HAP)
页面数量 17 个
权限声明 8 项
组件数量 8 个自定义组件
服务数量 11 个服务类
数据模型 6 个模型文件

6.2 首次构建遇到的 3 个问题及解决

6.2.1 问题一:无效权限名 ohos.permission.SCAN_WIFI

现象

首次构建时,module.json5 中声明了 ohos.permission.SCAN_WIFI 权限,但该权限在 HarmonyOS API 24 中不存在。

错误信息

构建时,Hvigor 在 ProcessProfile 阶段检测到无效的权限名,导致编译失败。

根因分析

ohos.permission.SCAN_WIFI 是一个不存在的权限名。HarmonyOS 中与 WiFi 相关的权限包括:

  • ohos.permission.GET_WIFI_INFO:获取 WiFi 连接信息
  • ohos.permission.SET_WIFI_INFO:设置 WiFi 信息
  • ohos.permission.GET_WIFI_LOCAL_MAC:获取设备 MAC 地址
  • ohos.permission.WIFI:WiFi 管理权限

IVGuard 只需要获取 WiFi 信息用于辅助定位,不需要扫描 WiFi,因此应该使用 GET_WIFI_INFO

解决方案

module.json5 中的权限名从 ohos.permission.SCAN_WIFI 修改为 ohos.permission.GET_WIFI_INFO

// 修改前
{
  "name": "ohos.permission.SCAN_WIFI",
  "reason": "$string:wifi_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "inuse"
  }
}

// 修改后
{
  "name": "ohos.permission.GET_WIFI_INFO"
}

同时,由于 GET_WIFI_INFOsystem_grant 权限,安装时自动授予,所以移除了 reasonusedScene 字段。

经验总结

  • HarmonyOS 的权限名是严格定义的,不存在"类似"的权限名
  • 声明权限前,务必查阅官方权限列表:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/permissions-overview
  • system_grant 权限不需要 reasonusedScene 字段
  • user_grant 权限必须reasonusedScene 字段
6.2.2 问题二:const 声明在 build() 方法中报错

现象

@Componentbuild() 方法中使用 const 声明变量,触发 ArkTS 编译器错误:

ERROR: 10905209 ArkTS Compiler Error
Error Message: Only UI component syntax can be written here.

根因分析

ArkTS 的 build() 方法有严格的语法限制,只能包含 UI 组件声明语法。这是 ArkTS 与普通 TypeScript 的关键区别之一。

build() 方法中,以下语法是不允许的:

// 错误 - 在 build() 中使用 const/let/var
build() {
  const title = 'IVGuard'
  Text(title)
}

// 错误 - 在 build() 中使用普通函数调用
build() {
  const data = this.processData()
  Text(data)
}

// 错误 - 在 build() 中使用解构
build() {
  const { width, height } = this.dimensions
  Text(`${width} x ${height}`)
}

解决方案

根据不同的使用场景,有以下替代方案:

方案 A:使用 @State@Prop

如果变量需要响应式更新,使用 @State

@Component
struct MyComponent {
  @State title: string = 'IVGuard'

  build() {
    Text(this.title)
  }
}

方案 B:使用内联表达式

如果变量只是简单的计算结果,直接内联:

build() {
  Text('IVGuard')  // 直接使用字面量
}

方案 C:使用 @Builder 函数

如果需要复用 UI 片段,使用 @Builder

@Builder
titleBuilder(title: string) {
  Text(title)
}

build() {
  this.titleBuilder('IVGuard')
}

方案 D:将逻辑移到 aboutToAppear

如果变量需要在初始化时计算,将逻辑移到生命周期回调中:

@Component
struct MyComponent {
  @State displayTitle: string = ''

  aboutToAppear(): void {
    this.displayTitle = this.processTitle()
  }

  build() {
    Text(this.displayTitle)
  }
}

经验总结

  • build() 方法中只能使用 UI 组件声明语法
  • 任何需要在 build() 中使用的动态数据,都应通过 @State/@Prop/@Link 管理
  • 复杂的计算逻辑应在 aboutToAppear 或其他方法中完成
  • @Builder 函数是复用 UI 逻辑的推荐方式
6.2.3 问题三:flexWrapRow 组件上不存在

现象

尝试在 Row 组件上使用 .flexWrap() 属性,编译报错提示该属性不存在。

根因分析

在 ArkUI 中,flexWrapFlex 容器组件的属性,而不是 RowColumn 的属性。RowColumn 是简化版的线性布局组件,不支持 flexWrap 属性。

// 错误 - Row 不支持 flexWrap
Row() {
  Text('Item 1')
  Text('Item 2')
  Text('Item 3')
}.flexWrap(FlexWrap.Wrap)  // 编译错误:Row 没有 flexWrap 属性

解决方案

使用 Flex 组件替代 RowFlex 是更灵活的布局容器,支持 flexWrapalignItemsjustifyContent 等完整的 Flexbox 布局属性:

// 正确 - 使用 Flex 组件
Flex({ wrap: FlexWrap.Wrap, direction: FlexDirection.Row }) {
  Text('Item 1')
  Text('Item 2')
  Text('Item 3')
}

Row/Column 与 Flex 的区别

特性 Row / Column Flex
布局方向 Row: 水平 / Column: 垂直 自由指定 direction
换行 不支持 支持 wrap: FlexWrap.Wrap
对齐方式 .alignItems() / .justifyContent() 构造参数中指定
性能 更优(简化的布局计算) 略慢(完整的 Flexbox 计算)
使用场景 简单的线性布局 需要换行或更复杂的布局

经验总结

  • 需要换行布局时,使用 Flex 而不是 Row
  • Row/Column 适合简单的线性布局,性能更好
  • Flex 适合需要换行或复杂对齐的场景
  • 在使用 ArkUI 组件属性前,应查阅 API 文档确认组件是否支持该属性

6.3 构建成功后的 WARN 处理

患者端-监控Tab_首页

IVGuard 构建成功后,日志中仍有 47 条 WARN。这些 WARN 不会导致构建失败,但值得关注和逐步修复。

6.3.1 getContext(this) 已弃用警告

警告信息

WARN: ArkTS:WARN File: .../pages/Index.ets:11:21
'getContext' has been deprecated.

影响范围:在 15 个页面文件中都使用了 getContext(this)

根因分析

getContext(this) 在 API 24 中已被标记为 deprecated(弃用),推荐使用 this.getUIContext().getHostContext()this.commonContext 等新 API。

当前处理

由于 getContext(this) 虽然已弃用但仍可正常工作,当前不做修改,后续版本迁移到新 API 即可。这种"可用但弃用"的状态在 HarmonyOS API 演进中很常见,通常会在 2-3 个大版本后才真正移除。

未来迁移方案

// 旧写法(已弃用)
const context = getContext(this) as common.UIAbilityContext

// 新写法(推荐)
const context = this.getUIContext().getHostContext() as common.UIAbilityContext
6.3.2 requestEnableNotification 已弃用警告

警告信息

WARN: ArkTS:WARN File: .../service/NotificationService.ets:42:35
'requestEnableNotification' has been deprecated.

根因分析

requestEnableNotification() 在 API 24 中已被弃用,推荐使用 notificationManager.requestEnableNotification() 或新的通知授权 API。

当前处理

getContext 类似,已弃用但仍可用。后续版本迁移即可。

6.3.3 Function may throw exceptions 警告

警告信息

WARN: ArkTS:WARN File: .../service/DataStore.ets:12:23
Function may throw exceptions. Special handling is required.

影响范围:DataStore.ets 中有 26 条此类警告。

根因分析

ArkTS 编译器检测到某些函数调用可能抛出异常,但代码中没有使用 try-catch 进行异常处理。涉及的函数主要是 preferences 模块的同步 API,如 getPreferencesSyncputSyncgetSyncflush 等。

当前处理

这些同步 API 在正常情况下不会抛出异常(除非参数严重错误),当前不处理。如果需要严格消除警告,可以添加 try-catch:

// 当前写法
static loadMedicines(): Medicine[] {
  const s = DataStore.getStore()
  const raw = s.getSync('medicines', '[]') as string
  return JSON.parse(raw) as Medicine[]
}

// 严格处理异常的写法
static loadMedicines(): Medicine[] {
  try {
    const s = DataStore.getStore()
    const raw = s.getSync('medicines', '[]') as string
    return JSON.parse(raw) as Medicine[]
  } catch (e) {
    hilog.error(0x0000, 'DataStore', 'Failed to load medicines: %{public}s', JSON.stringify(e))
    return []
  }
}
6.3.4 私有属性初始化警告

警告信息

WARN: ArkTS:WARN File: .../pages/MedicinePage.ets:81:13
Property 'onCardClick' is private and can not be initialized through the component constructor.

WARN: ArkTS:WARN File: .../pages/NurseHomePage.ets:146:15
Property 'onHandle' is private and can not be initialized through the component constructor.

根因分析

在 ArkUI 自定义组件中,private 修饰的属性不能通过组件构造器(即父组件调用子组件时的属性赋值)进行初始化。应该将 private 改为不限制访问修饰符(默认为 public)或使用 @Prop 等装饰器。

当前处理

警告不影响功能,但建议后续修复。将 private 改为默认访问级别或 @Prop 即可。

6.3.5 WARN 处理优先级建议
优先级 警告类型 数量 建议
P2 getContext 已弃用 15 下一版本迁移到新 API
P2 requestEnableNotification 已弃用 1 下一版本迁移到新 API
P3 函数可能抛出异常 26 逐步添加 try-catch
P3 私有属性初始化 2 修改访问修饰符

7. 常见构建问题与解决方案

7.1 权限配置错误

7.1.1 无效权限名

症状:构建失败,错误提示权限名不存在。

常见错误

// 错误:不存在的权限名
{ "name": "ohos.permission.SCAN_WIFI" }      // 不存在
{ "name": "ohos.permission.CAMERA_ACCESS" }   // 不存在
{ "name": "ohos.permission.READ_CALENDAR" }   // 不存在

// 正确:
{ "name": "ohos.permission.GET_WIFI_INFO" }
{ "name": "ohos.permission.CAMERA" }
{ "name": "ohos.permission.READ_CALENDAR" }   // 这个确实存在

解决方案

  1. 查阅官方权限列表确认权限名是否正确
  2. 注意权限名大小写敏感(caseSensitiveCheck: true
  3. 参考权限文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/permissions-overview
7.1.2 缺少 reason 字段

症状:构建警告或应用上架被驳回。

规则

  • user_grant 权限必须配置 reason 字段
  • system_grant 权限不需要 reason 字段
// 错误 - user_grant 权限缺少 reason
{
  "name": "ohos.permission.CAMERA"
  // 缺少 reason 和 usedScene
}

// 正确
{
  "name": "ohos.permission.CAMERA",
  "reason": "$string:camera_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "inuse"
  }
}
7.1.3 usedScene 缺失

症状:构建警告或应用上架审核不通过。

规则

  • user_grant 权限应配置 usedScene,声明权限使用的场景
  • when 字段可选值为 inuse(仅前台)或 always(前后台)
  • 优先使用 inuse,除非功能确实需要后台访问
// 错误 - usedScene 缺失
{
  "name": "ohos.permission.LOCATION",
  "reason": "$string:location_reason"
}

// 正确
{
  "name": "ohos.permission.LOCATION",
  "reason": "$string:location_reason",
  "usedScene": {
    "abilities": ["EntryAbility"],
    "when": "inuse"
  }
}

7.2 页面注册不一致

7.2.1 症状
  • 启动应用后白屏
  • 路由跳转时报错
  • 页面无法加载
7.2.2 根因

main_pages.json 中注册的页面与 entry/src/main/ets/pages/ 目录下的实际页面文件不匹配。

常见不一致场景

  1. 新增了页面文件但未在 main_pages.json 注册:新增了 SettingsPage.ets,但忘记在 main_pages.json 中添加 "pages/SettingsPage"
  2. main_pages.json 注册了不存在的页面:注册了 "pages/ProfilePage",但实际没有 ProfilePage.ets 文件
  3. 页面路径拼写错误main_pages.json 中写的是 "pages/Monitor",但文件名是 MonitorPage.ets
7.2.3 解决方案
  1. 每次新增页面时,同步更新 main_pages.json
  2. 构建前检查页面文件与注册表的对应关系
  3. 使用脚本自动检测不一致:
# 检查 pages 目录下的 .ets 文件
$pages = Get-ChildItem -Path "entry\src\main\ets\pages" -Filter "*.ets" |
         ForEach-Object { "pages/" + $_.BaseName }

# 读取 main_pages.json 中的注册页面
$registered = (Get-Content "entry\src\main\resources\base\profile\main_pages.json" |
               ConvertFrom-Json).src

# 对比差异
$missing = $pages | Where-Object { $_ -notin $registered }
$extra = $registered | Where-Object { $_ -notin $pages }

if ($missing) { Write-Host "未注册的页面: $missing" }
if ($extra) { Write-Host "不存在的页面: $extra" }

7.3 ArkTS 严格模式编译错误

7.3.1 使用了 any/unknown 类型

错误arkts-no-any-unknown

解决方案

// 错误
let data: any = response
function parse(input: unknown): string

// 正确 - 使用具体类型
let data: Record<string, Object> = response
function parse(input: string): string
7.3.2 在 build() 中使用了 const/let

错误Only UI component syntax can be written here

解决方案

// 错误
build() {
  const items = this.getList()
  ForEach(items, (item: string) => { Text(item) })
}

// 正确 - 使用 @State
@Component
struct MyComponent {
  @State items: string[] = []

  aboutToAppear() {
    this.items = this.getList()
  }

  build() {
    ForEach(this.items, (item: string) => { Text(item) })
  }
}
7.3.3 使用了对象字面量类型

错误arkts-no-obj-literals-as-types

解决方案

// 错误
type Config = { theme: string; fontSize: number }

// 正确 - 使用 class
class Config {
  theme: string = ''
  fontSize: number = 14
}
7.3.4 使用了结构类型而非类

错误arkts-no-structural-type

解决方案

// 错误 - 接口作为参数类型
function greet(person: { name: string }): string

// 正确 - 使用 class
class Person {
  name: string = ''
}
function greet(person: Person): string

7.4 资源引用错误

7.4.1 $r 路径错误

错误现象:运行时资源找不到,显示空白或默认值。

常见错误

// 错误 - 路径格式不正确
$r('string.camera_reason')       // 缺少模块前缀
$r('entry.string.camera_reason') // 正确但不带 app 前缀

// 正确
$r('app.string.camera_reason')   // 应用级资源
$r('entry.string.camera_reason') // 模块级资源
$r('app.media.layered_image')    // 图片资源
$r('app.color.start_window_background')  // 颜色资源

资源引用格式

  • $r('app.{type}.{name}'):引用 AppScope 下的应用级资源
  • $r('entry.{type}.{name}'):引用 entry 模块下的资源
  • {type} 可以是 stringcolorfloatmediaprofile
7.4.2 $rawfile 路径错误

$rawfile 用于引用 resources/rawfile/ 目录下的原始文件:

// 正确 - 引用 rawfile 下的文件
$rawfile('config/data.json')

// 错误 - 路径不存在
$rawfile('data.json')  // 如果文件在 rawfile/config/ 子目录下

注意事项

  • $rawfile 的路径相对于 resources/rawfile/ 目录
  • 不需要写 resources/rawfile/ 前缀
  • 支持 子目录路径

7.5 签名配置问题

7.5.1 真机调试签名缺失

症状:hdc install 安装 HAP 到真机时报签名错误。

解决方案

  1. 在 DevEco Studio 中配置自动签名
  2. 或手动配置 signingConfigs
7.5.2 签名证书过期

症状:签名失败,提示证书已过期。

解决方案

  1. 在 AppGallery Connect 中重新申请调试证书
  2. 更新 build-profile.json5 中的证书路径
7.5.3 签名类型不匹配

症状:签名失败,提示签名类型与设备系统不匹配。

解决方案

确保 signingConfigs 中的 type 与目标设备系统一致:

  • 目标为 HarmonyOS 设备:type: "HarmonyOS"
  • 目标为 OpenHarmony 设备:type: "OpenHarmony"

8. CI/CD 集成建议

8.1 命令行构建

8.1.1 hvigorw 命令行构建

Hvigor 的命令行接口是 CI/CD 集成的基础。所有在 DevEco Studio 中执行的构建操作,都可以通过命令行完成。

基本构建命令

# 构建整个 APP(包含所有模块)
hvigorw assembleApp --no-daemon

# 仅构建 entry 模块的 HAP
hvigorw assembleHap --no-daemon

# 指定 product 和 buildMode
hvigorw assembleApp -p product=default -p buildMode=debug --no-daemon

# 构建 release 包
hvigorw assembleApp -p product=default -p buildMode=release --no-daemon

# 清理构建产物后重新构建
hvigorw clean assembleApp --no-daemon

--no-daemon 参数的重要性

在 CI/CD 环境中,必须使用 --no-daemon 参数,原因如下:

  • Hvigor 默认启动守护进程(daemon)来加速后续构建
  • 守护进程在构建完成后不会自动退出,会占用内存
  • 在 CI/CD 流水线中,守护进程会导致构建任务"挂住",无法正常结束
  • --no-daemon 确保构建完成后进程立即退出

IVGuard 的 CI 构建命令

# 完整的 CI 构建脚本
$projectDir = "F:\projects\IVGuard"

# 1. 拉取最新代码
git pull origin main

# 2. 安装依赖
ohpm install

# 3. 清理旧构建产物
hvigorw clean --no-daemon

# 4. 构建 debug HAP
hvigorw assembleHap -p product=default -p buildMode=debug --no-daemon

# 5. 检查构建结果
$hapPath = "$projectDir\entry\build\default\outputs\default\entry-default-unsigned.hap"
if (Test-Path $hapPath) {
    Write-Host "BUILD SUCCESS: $hapPath"
} else {
    Write-Host "BUILD FAILED"
    exit 1
}
8.1.2 环境变量配置

在 CI/CD 环境中,需要确保以下工具可用:

# 设置 DevEco Studio 工具链路径
$devecoHome = "F:\DevEco Studio"
$env:PATH += ";$devecoHome\tools\node"
$env:PATH += ";$devecoHome\tools\hvigor\bin"
$env:PATH += ";$devecoHome\tools\ohpm\bin"
$env:PATH += ";$devecoHome\tools\hdc"

# 设置 SDK 路径
$env:HARMONYOS_SDK_HOME = "F:\HarmonyOS\Sdk"

# 设置 Node.js 选项(大项目可能需要更多内存)
$env:NODE_OPTIONS = "--max-old-space-size=8192"
8.1.3 构建日志保存

在 CI/CD 中,构建日志是排查问题的重要依据:

# 将构建日志保存到文件
hvigorw assembleApp --no-daemon 2>&1 | Tee-Object -FilePath "build-$(Get-Date -Format 'yyyyMMdd-HHmmss').log"

或使用 --log-path 参数(如果 hvigor 支持):

hvigorw assembleApp --no-daemon --log-path build.log

8.2 自动化签名配置

8.2.1 签名材料的准备

在 CI/CD 环境中,签名材料需要提前准备并安全存储:

需要的签名材料

文件 说明 获取方式
.p12 文件 密钥库文件 通过 DevEco Studio 或 keytool 生成
.cer 文件 证书文件 从 AppGallery Connect 下载
.p7b 文件 Profile 文件 从 AppGallery Connect 下载

签名材料的安全管理

# 签名材料应存放在安全的 CI/CD 密钥管理系统中
# 不要将签名材料提交到 Git 仓库!

# Jenkins 示例:使用 Credentials 存储签名密码
$certPath = Get-Credential -Message "Certificate Path"
$storePassword = $env:SIGNING_STORE_PASSWORD  # 从 CI 环境变量读取
$keyPassword = $env:SIGNING_KEY_PASSWORD       # 从 CI 环境变量读取
8.2.2 动态配置 signingConfigs

在 CI/CD 中,可以通过脚本动态生成 build-profile.json5 的签名配置:

// generate-signing-config.js
const fs = require('fs');
const path = require('path');

const buildProfilePath = path.join(__dirname, 'build-profile.json5');
let content = fs.readFileSync(buildProfilePath, 'utf-8');

const signingConfig = {
  name: 'default',
  type: 'HarmonyOS',
  material: {
    certpath: process.env.SIGNING_CERT_PATH,
    storePassword: process.env.SIGNING_STORE_PASSWORD,
    keyAlias: process.env.SIGNING_KEY_ALIAS || 'debugKey',
    keyPassword: process.env.SIGNING_KEY_PASSWORD,
    profile: process.env.SIGNING_PROFILE_PATH,
    signAlg: 'SHA256withECDSA',
    storeFile: process.env.SIGNING_STORE_FILE
  }
};

// 替换 signingConfigs
content = content.replace(
  /"signingConfigs":\s*\[\]/,
  `"signingConfigs": [${JSON.stringify(signingConfig, null, 2)}]`
);

fs.writeFileSync(buildProfilePath, content, 'utf-8');
console.log('Signing config generated successfully');
# CI 流水线中使用
node generate-signing-config.js
hvigorw assembleApp -p buildMode=release --no-daemon
8.2.3 使用自动签名(AGC)

如果使用华为 AppGallery Connect(AGC)自动签名:

  1. 在 AGC 中注册应用,获取 clientIdclientSecret
  2. 在 CI/CD 环境中配置这些凭证
  3. 构建时使用 AGC CLI 自动获取签名材料

8.3 构建产物归档

8.3.1 产物路径

IVGuard 构建后的产物路径:

# HAP 包
entry/build/default/outputs/default/entry-default-unsigned.hap  # 未签名 HAP
entry/build/default/outputs/default/app/entry-default.hap       # 已签名 HAP

# APP 包
build/outputs/default/IVGuard-default-unsigned.app              # 未签名 APP
build/outputs/default/IVGuard-default-signed.app                # 已签名 APP

# Source Map(用于线上崩溃还原)
entry/build/default/outputs/default/mapping/sourceMaps.map

# 构建日志
entry/build/default/outputs/default/entry-default-build.log
8.3.2 归档脚本
# 归档构建产物
$buildId = Get-Date -Format 'yyyyMMdd-HHmmss'
$archiveDir = "F:\archives\IVGuard\$buildId"

New-Item -ItemType Directory -Path $archiveDir -Force

# 复制 HAP 包
Copy-Item "entry\build\default\outputs\default\entry-default-unsigned.hap" $archiveDir

# 复制 APP 包(如果存在)
if (Test-Path "build\outputs\default\IVGuard-default-unsigned.app") {
    Copy-Item "build\outputs\default\IVGuard-default-unsigned.app" $archiveDir
}

# 复制 Source Map
Copy-Item "entry\build\default\outputs\default\mapping\sourceMaps.map" $archiveDir

# 复制构建日志
Copy-Item "build.log" $archiveDir -ErrorAction SilentlyContinue

# 生成构建信息文件
$buildInfo = @{
    buildId = $buildId
    versionName = "1.0.0"
    versionCode = 1000000
    bundleName = "com.example.ivguard"
    buildTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
    gitCommit = git rev-parse HEAD
    buildMode = "debug"
} | ConvertTo-Json

Set-Content -Path "$archiveDir\build-info.json" -Value $buildInfo

Write-Host "Archived to: $archiveDir"
8.3.3 产物命名规范

建议使用以下命名规范区分不同构建产物:

IVGuard-{versionName}-{buildMode}-{buildId}.hap
IVGuard-{versionName}-{buildMode}-{buildId}.app

# 示例
IVGuard-1.0.0-debug-20260803120000.hap
IVGuard-1.0.0-release-20260803120000.app

8.4 多渠道打包

8.4.1 多 Product 配置

通过 build-profile.json5products 数组,可以配置多个构建产物:

{
  "app": {
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)",
        "runtimeOS": "HarmonyOS"
      },
      {
        "name": "nurse",
        "signingConfig": "nurse",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)",
        "runtimeOS": "HarmonyOS",
        "buildOption": {
          "arkOptions": {
            "compilerOptions": {
              "arkuiBuild": "partial"
            }
          }
        }
      }
    ]
  }
}
8.4.2 构建不同渠道包
# 构建默认渠道包
hvigorw assembleHap -p product=default -p buildMode=release --no-daemon

# 构建护士专用渠道包
hvigorw assembleHap -p product=nurse -p buildMode=release --no-daemon
8.4.3 环境隔离配置

在多人协作开发时,每个开发者可以配置自己的签名和 product:

// build-profile.json5
{
  "app": {
    "signingConfigs": [
      {
        "name": "dev_zhang",
        "type": "HarmonyOS",
        "material": {
          "certpath": "D:/Signing/zhang/cert.cer",
          "storeFile": "D:/Signing/zhang/key.p12",
          // ...
        }
      },
      {
        "name": "dev_li",
        "type": "HarmonyOS",
        "material": {
          "certpath": "D:/Signing/li/cert.cer",
          "storeFile": "D:/Signing/li/key.p12",
          // ...
        }
      }
    ],
    "products": [
      {
        "name": "zhang",
        "signingConfig": "dev_zhang",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)",
        "runtimeOS": "HarmonyOS"
      },
      {
        "name": "li",
        "signingConfig": "dev_li",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}
8.4.4 GitHub Actions 集成示例

以下是一个完整的 GitHub Actions 工作流示例,用于自动化构建 IVGuard:

# .github/workflows/build.yml
name: IVGuard Build

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: windows-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Setup DevEco CLI
        run: npm install -g @deveco/deveco-cli@latest

      - name: Install Dependencies
        run: ohpm install
        working-directory: F:\projects\IVGuard

      - name: Build Debug HAP
        run: |
          hvigorw assembleHap -p product=default -p buildMode=debug --no-daemon
        working-directory: F:\projects\IVGuard
        env:
          NODE_OPTIONS: --max-old-space-size=8192

      - name: Archive Build Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: IVGuard-debug-hap
          path: F:\projects\IVGuard\entry\build\default\outputs\default\entry-default-unsigned.hap

      - name: Build Release APP
        if: github.ref == 'refs/heads/main'
        run: |
          hvigorw assembleApp -p product=default -p buildMode=release --no-daemon
        working-directory: F:\projects\IVGuard
        env:
          NODE_OPTIONS: --max-old-space-size=8192
          SIGNING_CERT_PATH: ${{ secrets.SIGNING_CERT_PATH }}
          SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
          SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
          SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
          SIGNING_PROFILE_PATH: ${{ secrets.SIGNING_PROFILE_PATH }}
          SIGNING_STORE_FILE: ${{ secrets.SIGNING_STORE_FILE }}
8.4.5 Jenkins 集成示例
// Jenkinsfile
pipeline {
    agent { label 'windows' }

    environment {
        PROJECT_DIR = 'F:\\projects\\IVGuard'
        NODE_OPTIONS = '--max-old-space-size=8192'
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Install Dependencies') {
            steps {
                bat "ohpm install"
            }
        }

        stage('Build Debug') {
            steps {
                bat "hvigorw assembleHap -p product=default -p buildMode=debug --no-daemon"
            }
        }

        stage('Archive') {
            steps {
                archiveArtifacts artifacts: "entry/build/default/outputs/default/*.hap", allowEmptyArchive: true
            }
        }

        stage('Deploy to Test Device') {
            when {
                branch 'develop'
            }
            steps {
                bat "hdc install -r entry/build/default/outputs/default/entry-default-unsigned.hap"
            }
        }
    }

    post {
        failure {
            mail to: 'team@example.com',
                 subject: "IVGuard Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                 body: "Build failed. Check console output at ${env.BUILD_URL}"
        }
    }
}

附录

A. IVGuard 项目文件速查表

文件路径 类型 说明
AppScope/app.json5 配置 应用标识(bundleName、version)
AppScope/resources/base/element/string.json 资源 应用级字符串资源
build-profile.json5 配置 工程级构建配置
entry/build-profile.json5 配置 模块级构建配置
entry/src/main/module.json5 配置 模块声明(Ability、权限)
entry/src/main/resources/base/profile/main_pages.json 配置 页面路由注册
entry/src/main/resources/base/profile/backup_config.json 配置 备份恢复配置
entry/src/main/resources/base/element/string.json 资源 模块级字符串资源
entry/src/main/resources/base/element/color.json 资源 颜色资源
entry/src/main/resources/base/element/float.json 资源 尺寸资源
entry/src/main/ets/entryability/EntryAbility.ets 源码 入口 Ability
entry/src/main/ets/entrybackupability/EntryBackupAbility.ets 源码 备份扩展
entry/src/main/ets/pages/*.ets 源码 17 个页面
entry/src/main/ets/component/*.ets 源码 8 个自定义组件
entry/src/main/ets/service/*.ets 源码 11 个服务类
entry/src/main/ets/model/*.ets 源码 6 个数据模型
entry/oh-package.json5 配置 模块级依赖声明
entry/obfuscation-rules.txt 配置 代码混淆规则
entry/hvigorfile.ts 脚本 模块级构建脚本
oh-package.json5 配置 工程级依赖声明
oh-package-lock.json5 锁文件 依赖锁文件
hvigorfile.ts 脚本 工程级构建脚本
hvigor/hvigor-config.json5 配置 构建工具配置
code-linter.json5 配置 代码检查配置

B. 常用构建命令速查

命令 说明
hvigorw assembleHap 构建 HAP 包
hvigorw assembleApp 构建 APP 包
hvigorw assembleHar 构建 HAR 包
hvigorw assembleHsp 构建 HSP 包
hvigorw clean 清理构建产物
hvigorw clean assembleApp --no-daemon 清理后全量构建
hvigorw -p buildMode=debug assembleHap 构建 debug HAP
hvigorw -p buildMode=release assembleHap 构建 release HAP
ohpm install 安装依赖
ohpm install <package> 安装指定依赖
hdc install <hap> 安装 HAP 到设备
hdc hilog 查看设备日志
hdc list targets 列出已连接设备

C. 权限速查表

权限 授权类型 说明
ohos.permission.CAMERA user_grant 相机
ohos.permission.NFC_TAG user_grant NFC 标签
ohos.permission.NOTIFICATION_CONTROLLER user_grant 通知控制
ohos.permission.KEEP_BACKGROUND_RUNNING user_grant 后台保活
ohos.permission.VIBRATE system_grant 振动
ohos.permission.LOCATION user_grant 精确定位
ohos.permission.APPROXIMATELY_LOCATION user_grant 粗略定位
ohos.permission.GET_WIFI_INFO system_grant WiFi 信息
ohos.permission.INTERNET system_grant 网络访问
ohos.permission.READ_MEDIA user_grant 读取媒体文件
ohos.permission.WRITE_MEDIA user_grant 写入媒体文件
ohos.permission.MICROPHONE user_grant 麦克风
ohos.permission.BLUETOOTH system_grant 蓝牙
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐