引言
在 HarmonyOS Next 的應用開發中,常常需要針對不同環境(測試、預發、線上)或不同簽名(調試、正式)輸出多個 APP/HAP 包。雖然 HarmonyOS 提供了多目標構建(Multi-Target Build)能力,可以在同一項目里配置多個 product 并生成不同包名的多個產物,但某些深度定制(如動態修改 module.json5
中的 client_id
、app_id
,或基于構建時間改寫輸出包名)仍需借助自定義 Hvigor 插件來完成。
結合示例項目,演示如何通過 Hvigor 插件配合多目標構建,實現:
- 統一管理各環境的 API 地址、埋點地址等配置;
- 動態注入
clientId
、appId
等應用信息; - 基于構建時間和 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_PLUGIN
、OHOS_HAP_PLUGIN
context.getAppJsonOpt()
/getModuleJsonOpt()
:讀取app.json5
或module.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 命名
為了腳本中便于解析,我們約定 product
的 name
采用下劃線分隔、固定格式:
<應用標識>_<簽名標識>_<環境標識>
例如: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 配置… */
]
Tip:
buildProfileFields
下的字段會被注入到應用運行時,便于在代碼中讀取環境信息。
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.json5
、build-profile.json5
,免去手動切換; - 定制產物:基于構建時間與 product 名稱,自定義輸出包名,方便版本追蹤與線上排查。
未來可在插件中進一步添加:
- 構建報告自動上傳到 CI/CD 平臺;
- 自動生成構建差異對比的 HTML 報告;
- 與 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包前言 做移動端開發時,不可避免的會遇到 - 掘金