本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
鸿蒙系统中,可以通过构建配置变体实现类似Android的多环境打包方案,允许为不同环境(如test/prod)生成不同的安装包。
一、鸿蒙构建变体实现架构
二、具体实现步骤
1. 在build-profile.json5
中定义环境维度
// build-profile.json5
{
"app": {
"signingConfigs": [...],
"compileSdkVersion": 9,
"compatibleSdkVersion": 9,
"products": [
{
"name": "prod",
"signingConfig": "default",
"compileSdkVersion": 9,
"env": { "APP_ENV": "prod" },
"resourceGroups": [
{
"name": "config",
"src": "src/main/resources/rawfile/prod"
}
]
},
{
"name": "test",
"signingConfig": "test",
"compileSdkVersion": 9,
"env": { "APP_ENV": "test" },
"resourceGroups": [
{
"name": "config",
"src": "src/main/resources/rawfile/test"
}
]
}
]
}
}
2. 配置环境差异化资源
src/
main/
resources/
rawfile/
prod/
config.json
test/
config.json
prod/config.json:
{
"apiBaseUrl": "https://api.example.com",
"logLevel": "warn"
}
test/config.json:
{
"apiBaseUrl": "https://test.api.example.com",
"logLevel": "debug"
}
3. 创建环境配置文件加载器
// src/main/ets/utils/EnvConfigLoader.ts
import featureAbility from '@ohos.ability.featureAbility';
class EnvConfigLoader {
private static configCache: Record<string, any> | null = null;
static async getConfig(): Promise<Record<string, any>> {
if (this.configCache) return this.configCache;
try {
const context = featureAbility.getContext();
const rawFile = await context.resourceManager.getRawFileContent('config.json');
const content = String.fromCharCode.apply(null, new Uint8Array(rawFile));
this.configCache = JSON.parse(content);
return this.configCache!;
} catch (error) {
console.error('环境配置加载失败', error);
return {};
}
}
}
export default EnvConfigLoader;
4. 在应用启动时加载配置
// src/main/ets/entryability/EntryAbility.ts
import EnvConfigLoader from '../utils/EnvConfigLoader';
export default class EntryAbility extends Ability {
async onCreate() {
console.info('EntryAbility onCreate');
// 加载环境配置
const config = await EnvConfigLoader.getConfig();
// 初始化应用服务
this.initAppServices(config);
}
initAppServices(config: Record<string, any>) {
// 初始化网络服务
NetworkService.init({
baseUrl: config.apiBaseUrl || 'https://default.api'
});
// 初始化日志服务
Logger.setLevel(config.logLevel || 'info');
console.info(`应用启动完成,当前环境: ${config.appEnv}`);
}
}
5. 配置不同环境的签名文件
创建测试环境签名配置:
// build-profile.json5
{
"signingConfigs": [
{
"name": "default",
"material": {
"certpath": "prod-cert.p7b",
"storePassword": "******",
"keyAlias": "prodKey",
"keyPassword": "******"
}
},
{
"name": "test",
"material": {
"certpath": "test-cert.p7b",
"storePassword": "******",
"keyAlias": "testKey",
"keyPassword": "******"
}
}
]
}
6. 配置打包命令
在package.json
中添加脚本命令:
{
"scripts": {
"build:prod": "hvigor assembleProd",
"build:test": "hvigor assembleTest",
"build:debug:prod": "hvigor assembleDebugProd",
"build:debug:test": "hvigor assembleDebugTest"
}
}
在hvigorfile.js
中配置任务:
// hvigorfile.js
const {
TaskManager,
Task,
HvigorContext,
TaskType
} = require('@ohos/hvigor-base');
module.exports = {
tasks: {
assembleProd: Task.create({
name: 'assembleProd',
type: TaskType.PACKAGE,
run: async (ctx) => {
ctx.setVariants('prod');
await ctx.executePackage();
}
}),
assembleTest: Task.create({
name: 'assembleTest',
type: TaskType.PACKAGE,
run: async (ctx) => {
ctx.setVariants('test');
await ctx.executePackage();
}
})
}
};
三、高级功能:差异化代码
1. 使用环境变量控制代码逻辑
// 在build-profile.json5中定义的环境变量可以通过process.env访问
const isTestEnv = process.env.APP_ENV === 'test';
function debugFeatures() {
if (isTestEnv) {
// 测试环境专用功能
enableDebugTools();
showEnvBanner();
}
}
2. 差异化资源文件
在src/main/resources
目录下创建环境专属资源目录:
resources/
base/
element/
strings.json
test/
element/
strings.json # 测试环境专属文案
配置资源目录映射:
// build-profile.json5
{
"resourceGroups": [
{
"name": "environment",
"src": {
"prod": "src/main/resources/base",
"test": "src/main/resources/test"
}
}
]
}
3. 环境专用页面
在src/main/ets/pages
下创建环境专用页面目录:
pages/
common/
HomePage.ets
test/
DebugPanel.ets # 测试环境专用页面
在入口文件中条件加载:
// MainAbility.ets
import { isTestEnv } from './utils/EnvUtils';
@Entry
@Component
struct MainPage {
build() {
if (isTestEnv) {
Column() {
TestEnvBanner() // 测试环境专属组件
HomePage()
DebugPanel() // 测试环境调试面板
}
} else {
HomePage()
}
}
}
四、构建和部署流程
1. 构建不同环境的HAP包
# 构建生产环境发布包
npm run build:prod
# 构建测试环境发布包
npm run build:test
# 构建测试环境调试包
npm run build:debug:test
2. 输出路径差异
构建结果将输出到不同目录:
build/
outputs/
prod/
entry_prod_signed.hap
test/
entry_test_signed.hap
3. 安装包识别
环境 | 文件名格式 | 包名后缀 | 签名证书 |
---|---|---|---|
生产环境 | entry_prod_release.hap | .prod | 正式证书 |
测试环境 | entry_test_debug.hap | .test | 测试证书 |
五、与Android变体的对比
功能 | Android (Gradle) | 鸿蒙 (Hvigor) |
---|---|---|
环境维度定义 | productFlavors | products配置 |
差异化资源 | src/prod/res | resourceGroups配置 |
环境变量注入 | buildConfigField | env字段 |
签名配置 | signingConfig | signingConfigs |
差异化代码 | 源集(src/prod/java) | 条件编译(process.env) |
构建命令 | assembleProdRelease | hvigor assembleProd |
依赖管理 | prodImplementation | 通过oh-package管理 |
六、建议
- 环境隔离原则
// 确保不同环境完全隔离
"products": [
{
"name": "prod",
"dependencies": {
"analytics": "prod-sdk@1.2.0"
}
},
{
"name": "test",
"dependencies": {
"analytics": "mock-sdk@1.0.0"
}
}
]
2. 环境检测
// 运行时环境验证
async function verifyEnvironment() {
const expected = process.env.APP_ENV;
const actual = await EnvConfigLoader.getEnvironment();
if (expected !== actual) {
throw new Error(`环境不匹配! 打包环境:${expected}, 运行环境:${actual}`);
}
}
七、常见问题
问题1:环境变量未注入
# 确保在hvigor命令中指定变体
hvigor assembleProd --variant prod
问题2:资源文件冲突
// 使用resourceGroups命名空间隔离
"resourceGroups": [
{
"name": "env_config",
"src": "src/main/resources/rawfile/${product.name}"
}
]
问题3:多模块环境同步
// 在根build-profile.json5中统一配置
"subprojects": {
"entry": {
"products": {
"prod": {},
"test": {}
}
},
"feature": {
"products": {
"prod": {},
"test": {}
}
}
}
这种构建可以实现:
- 为不同环境生成独立的安装包
- 实现代码和资源的差异化编译
- 保持相同的代码库管理多个环境
- 通过自动化命令简化构建流程
- 确保生产环境和测试环境完全隔离
完全符合类似Android变体的需求。