当前位置: 首页 > news >正文

一键多环境构建——用 Hvigor 玩转 HarmonyOS Next

引言

在 HarmonyOS Next 的应用开发中,常常需要针对不同环境(测试、预发、线上)或不同签名(调试、正式)输出多个 APP/HAP 包。虽然 HarmonyOS 提供了多目标构建(Multi-Target Build)能力,可以在同一项目里配置多个 product 并生成不同包名的多个产物,但某些深度定制(如动态修改 module.json5 中的 client_idapp_id,或基于构建时间改写输出包名)仍需借助自定义 Hvigor 插件来完成。

结合示例项目,演示如何通过 Hvigor 插件配合多目标构建,实现:

  1. 统一管理各环境的 API 地址、埋点地址等配置;
  2. 动态注入 clientIdappId 等应用信息;
  3. 基于构建时间和 product 名称,定制化输出包名,便于后续上架和线上排查。

一、在项目中使用 Hvigor 自定义插件

1. Hvigor 插件的入口

在 HarmonyOS 项目根目录存在入口文件 hvigorfile.ts,将自定义 task 注册到 plugins 中:

// 文件:hvigorfile.ts
import { buildSchemaProcessing } from './scripts/build-schema-processing';export default {system: appTasks,             // Hvigor 内置任务,不可修改plugins: [buildSchemaProcessing()     // 自定义插件:动态修改 module.json5、build-profile.json5 等]
};

点击 DevEco Studio 上方的 Sync Now 或执行 hvigor sync,即可将插件加载到构建流程中。


2. Hvigor 常用 API 概览

在自定义插件代码中,我们通常会用到以下核心 API:

  • hvigor.getRootNode():获取根节点,进而拿到各插件上下文
  • rootNode.getContext(pluginId):获取指定插件的上下文对象,例如 OHOS_APP_PLUGINOHOS_HAP_PLUGIN
  • context.getAppJsonOpt() / getModuleJsonOpt():读取 app.json5module.json5 的 AST 对象
  • context.getBuildProfileOpt():读取根目录 build-profile.json5 的 AST 对象
  • context.setAppJsonOpt() / setBuildProfileOpt():将修改后的 AST 写回,生效于后续构建
// 示例:读取上下文
const root = hvigor.getRootNode();
const appCtx = root.getContext(OhosPluginId.OHOS_APP_PLUGIN) as OhosAppContext;
const hapCtx = root.getContext(OhosPluginId.OHOS_HAP_PLUGIN) as OhosAppContext;
const appJson = appCtx.getAppJsonOpt();
const modJson = hapCtx.getModuleJsonOpt();
const buildProfile = appCtx.getBuildProfileOpt();// 修改后需要重新 set 回去
appCtx.setAppJsonOpt(appJson);
appCtx.setBuildProfileOpt(buildProfile);

在这里插入图片描述


二、结合多目标构建与自定义插件 实现环境切换

1. 规范 Product 命名

为了脚本中便于解析,我们约定 productname 采用下划线分隔、固定格式:

<应用标识>_<签名标识>_<环境标识>
例如:demo1_debug_test、demo1_release_preRelease、demo2_debug_official

build-profile.json5 中配置多个 product:

"products": [{"name": "demo1_debug_test","signingConfig": "debug","buildOption": {"arkOptions": {"buildProfileFields": {"productsName": "demo1","buildTime": "","apiUrl": "","trackApiUrl": ""}}}},{"name": "demo1_release_officiallyReleased","signingConfig": "release","buildOption": { /* 同上 */ }},/* 更多 product 配置… */
]

TipbuildProfileFields 下的字段会被注入到应用运行时,便于在代码中读取环境信息。


2. 本地配置文件:config.json

将各环境的 API 地址,以及各应用的 clientId/appId 信息集中管理:

{"environmentInfo": {"test": {"apiUrl": "https://api.test.com","trackApiUrl": "https://stat.test.com"},"preRelease": {"apiUrl": "https://api-pre.example.com","trackApiUrl": "https://stat-pre.example.com"},"officiallyReleased": {"apiUrl": "https://api.prod.com","trackApiUrl": "https://stat.prod.com"}},"appConfigInfo": {"demo1": {"clientId": "111898773","appId": "5765880207855284373"},"demo2": {"clientId": "6917571239128090930","appId": "6917571239128090930"}}
}

并提供两个辅助函数,在插件中使用:

// scripts/config.ts
import cfgRaw from '../config.json';
import { format } from 'date-fns';export function formatBuildTime(date = new Date()): string {const pad = (n: number) => String(n).padStart(2, '0');return `${date.getFullYear()}-${pad(date.getMonth()+1)}-${pad(date.getDate())}`+ `_${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;
}export function getLocalConfig() {const cfg = JSON.parse(JSON.stringify(cfgRaw));cfg.buildTime = formatBuildTime();return cfg;
}

3. 插件实现核心逻辑

build-schema-processing.ts 中,利用 Hvigor 生命周期钩子完成配置注入与产物重命名。

// scripts/build-schema-processing.ts
import { getLocalConfig } from './config';export function buildSchemaProcessing() {const localCfg = getLocalConfig();let currentProduct = '', versionName = '', bundleName = '', appConfig: any;return {pluginId: 'custom-build-processor',apply(hvigor) {hvigor.getRootNode().afterNodeEvaluate(root => {// 获取上下文const appCtx = root.getContext(OhosPluginId.OHOS_APP_PLUGIN) as OhosAppContext;const hapCtx = root.getContext(OhosPluginId.OHOS_HAP_PLUGIN) as OhosAppContext;const buildProfile = appCtx.getBuildProfileOpt();const appJson = appCtx.getAppJsonOpt();const modJson = hapCtx.getModuleJsonOpt();// 当前 product 信息currentProduct = appCtx.getCurrentProduct().getProductName() || '';versionName = appJson.app.versionName;const productKeys = currentProduct.split('_');const appKey = productKeys[0];const envKey = productKeys[2];appConfig = localCfg.appConfigInfo[appKey];const envConfig = localCfg.environmentInfo[envKey];// 注入 app.json5 中的 clientId、appIdif (modJson) {modJson['module']['appId'] = appConfig.appId;modJson['module']['clientId'] = appConfig.clientId;hapCtx.setModuleJsonOpt(modJson);}// 遍历各 product,注入环境 & 时间 & 重命名 artifact(buildProfile.app.products || []).forEach((prd: any) => {prd.buildOption.arkOptions.buildProfileFields.buildTime  = localCfg.buildTime;prd.buildOption.arkOptions.buildProfileFields.apiUrl     = envConfig.apiUrl;prd.buildOption.arkOptions.buildProfileFields.trackApiUrl= envConfig.trackApiUrl;const suffix = `${appKey}_${versionName}_${localCfg.buildTime}`;if (prd.name === currentProduct) {bundleName = prd.bundleName || appJson.app.bundleName;}prd.output.artifactName = `AtomicPlatform-${suffix}`;});appCtx.setBuildProfileOpt(buildProfile);});hvigor.buildFinished(() => {console.log(`📅 构建时间: ${localCfg.buildTime}`);console.log(`📦 当前产物: ${currentProduct}`);console.log(`🔖 包名(bundleName): ${bundleName}`);console.log(`🆔 ClientID: ${appConfig.clientId}`);console.log(`🆔 AppID: ${appConfig.appId}`);});}};
}

在这里插入图片描述


三、运行效果与日志验证

执行 hvigor build,在日志中即可看到注入与重命名结果:

====================== 编译包信息 ======================
📅 构建时间:  2025-04-24_09-32-05
📦 当前产物: demo2_debug_test
🔖 包名: com.atomicservice.6917571239128090930
🆔 ClientID: 6917571239128090930
🆔 AppID: 6917571239128090930
====================================================

同时,输出的 HAP 包会命名为:

AtomicPlatform-demo2_1.0.0_2025-04-24_09-32-05.hap

通过上述方式,每次在不同 product 与环境下编译,都能自动完成配置注入与产物重命名,极大提升了多环境多签名的开发与发布效率。


四、总结

  • 统一管理:将环境信息、应用信息集中在 config.json,便于维护与扩展;
  • 自动注入:借助 Hvigor 插件,在构建阶段动态修改 module.json5build-profile.json5,免去手动切换;
  • 定制产物:基于构建时间与 product 名称,自定义输出包名,方便版本追踪与线上排查。

未来可在插件中进一步添加:

  1. 构建报告自动上传到 CI/CD 平台;
  2. 自动生成构建差异对比的 HTML 报告;
  3. 与 Git 提交、发布流程集成,构建完成自动触发审核或推送。

五、源码仓库

仓库分支参考:feature/hvigorfileBuild

仓库地址:MultiBuildDemo: 构建多目标产物 - Gitee.com

六、参考文档

扩展构建-编译构建-DevEco Studio - 华为HarmonyOS开发者

HarmonyOS Next 编译之如何构建不同包名应用在日常的开发中涉及到多签名和多产物构建输出时手动切换签名文件和包 - 掘金

HarmonyOS多环境+多渠道+自定义路径输出+自定义名称一键打app和hap包前言 做移动端开发时,不可避免的会遇到 - 掘金
sumer/cn/doc/harmonyos-guides/ide-build-expanding)

HarmonyOS Next 编译之如何构建不同包名应用在日常的开发中涉及到多签名和多产物构建输出时手动切换签名文件和包 - 掘金

HarmonyOS多环境+多渠道+自定义路径输出+自定义名称一键打app和hap包前言 做移动端开发时,不可避免的会遇到 - 掘金

相关文章:

  • Docker 部署 Redis:快速搭建高效缓存服务
  • 解决yarn install 报错 error \node_modules\electron: Command failed.
  • 【PVCodeNet】《Palm Vein Recognition Network Combining Transformer and CNN》
  • Unity MR开发:探索混合现实的无限可能 (VisionPro和HoloLens 2 对比)
  • 注意力机制:Transformer如何用“数学凝视“统治AI?
  • 深度学习物理信息神经网络PINN+大模型辅助编程​
  • continue插件实现IDEA接入本地离线部署的deepseek等大模型
  • Kafka消息可视化工具Offset Explorer
  • windows中kafka4.0集群搭建
  • STM32F103系列单片机寄存器操作和标准库操作
  • SpringCloud微服务架构设计与实践 - 面试实战
  • Web3中心化交易所钱包-批量地址生成业务
  • 【RocketMq源码篇-01】环境搭建、基本使用、可视化界面
  • ES6 模块化 与 CommonJS 的核心概念解析
  • linux系统如何锁定一个用户?又如何解锁该用户呢
  • 项目中数据结构为什么用数组,不用List
  • 【C++ 类和数据抽象】static 类成员
  • Qt —— 在Linux下试用QWebEngingView出现的Js错误问题解决(附上四种解决办法)
  • PostgreSQL-日志管理介绍
  • 【网络入侵检测】基于Suricata源码分析NFQ IPS模式实现
  • 申花四连胜领跑中超,下轮榜首大战对蓉城将是硬仗考验
  • 2025年全国贸易摩擦应对工作会议在京召开
  • 美银证券前董事总经理胡霁光履新,任摩根士丹利中国区副主席
  • 朝中社发表评论文章,谴责美军部署B1-B轰炸机至日本
  • 见微知沪|最大力度消费补贴,最大程度满足人们对美好生活的向往
  • 2025全球智慧城市指数排名揭晓,阿布扎比跃升至第五位