Appearance
10_Engineering/05 - 多环境配置
1. 为什么需要多环境配置
HarmonyOS 应用通常需要支持多种运行环境:
| 环境 | 说明 | API Level | 签名 |
|---|---|---|---|
| Dev(开发环境) | 本地开发调试 | API 12 | Debug 签名 |
| Beta(测试环境) | QA 测试、内部测试 | API 11/12 | 测试签名 |
| Release(生产环境) | AppGallery 上架 | API 11+ | Release 签名 |
| Staging(预发布) | 灰度发布 | API 11+ | 预发布签名 |
不同环境需要不同的配置:
- API Level(编译版本)
- API 地址(开发/测试/生产接口)
- 签名证书
- 功能开关
- Debug 日志开关
- 第三方 SDK 配置
2. 多环境配置方案
2.1 方案一:products 配置(推荐)
json5
// build-profile.json5
{
"app": {
"signingConfigs": [
{
"name": "debug",
"type": "HarmonyOS",
"material": { "certpath": "debug.cer", "storeFile": "debug.p12", ... }
},
{
"name": "release",
"type": "HarmonyOS",
"material": { "certpath": "release.cer", "storeFile": "release.p12", ... }
}
],
"products": [
{
"name": "dev",
"signingConfig": "debug",
"compatibleSdkVersion": "12",
"runtimeOS": "HarmonyOS",
"bundleName": "com.example.myapp.dev",
"minAPIVersion": 12,
"bundleType": "app",
"debug": true
},
{
"name": "beta",
"signingConfig": "debug",
"compatibleSdkVersion": "11",
"runtimeOS": "HarmonyOS",
"bundleName": "com.example.myapp.beta",
"minAPIVersion": 11,
"bundleType": "app",
"debug": true
},
{
"name": "release",
"signingConfig": "release",
"compatibleSdkVersion": "11",
"runtimeOS": "HarmonyOS",
"bundleName": "com.example.myapp",
"minAPIVersion": 11,
"bundleType": "app",
"debug": false
}
],
"buildModeSet": [
{ "name": "debug" },
{ "name": "release" }
]
}
}2.2 方案二:多环境配置类
typescript
// common/src/main/ets/config/EnvironmentConfig.ets
/** 环境配置 - 通过编译期 define 注入 */
export class EnvironmentConfig {
/** 当前环境 */
static readonly ENV: string = 'DEV'; // DEV / BETA / RELEASE
/** API 基础地址 */
static readonly API_BASE_URL: string = this.getApiBaseUrl();
/** 是否开启 Debug 日志 */
static readonly DEBUG_LOG: boolean = this.ENV !== 'RELEASE';
/** 是否开启 Mock 数据 */
static readonly MOCK_ENABLED: boolean = this.ENV === 'DEV';
/** 应用 ID(不同环境不同) */
static readonly APP_ID: string = this.getAppId();
private static getApiBaseUrl(): string {
switch (this.ENV) {
case 'DEV': return 'https://dev-api.example.com';
case 'BETA': return 'https://beta-api.example.com';
case 'RELEASE': return 'https://api.example.com';
default: return 'https://api.example.com';
}
}
private static getAppId(): string {
switch (this.ENV) {
case 'DEV': return 'app-dev-001';
case 'BETA': return 'app-beta-001';
case 'RELEASE': return 'app-release-001';
default: return 'app-release-001';
}
}
}2.3 方案三:编译期 define 注入
json5
// entry/build-profile.json5
{
"buildOptionSet": [
{
"name": "dev",
"arkOptions": {
"arkFlags": [
"--define=ENV:DEV",
"--define=API_BASE_URL:https://dev-api.example.com"
]
}
},
{
"name": "beta",
"arkOptions": {
"arkFlags": [
"--define=ENV:BETA",
"--define=API_BASE_URL:https://beta-api.example.com"
]
}
},
{
"name": "release",
"arkOptions": {
"arkFlags": [
"--define=ENV:RELEASE",
"--define=API_BASE_URL:https://api.example.com"
]
}
}
]
}typescript
// 通过 globalThis 访问编译期注入的配置
if (globalThis['ENV'] === 'DEV') {
EnvironmentConfig.ENV = 'DEV';
EnvironmentConfig.API_BASE_URL = 'https://dev-api.example.com';
} else if (globalThis['ENV'] === 'BETA') {
EnvironmentConfig.ENV = 'BETA';
EnvironmentConfig.API_BASE_URL = 'https://beta-api.example.com';
} else {
EnvironmentConfig.ENV = 'RELEASE';
EnvironmentConfig.API_BASE_URL = 'https://api.example.com';
}3. 环境变量使用
3.1 网络请求配置
typescript
// common/src/main/ets/http/BaseHttpClient.ets
import { EnvironmentConfig } from '../config/EnvironmentConfig';
export class BaseHttpClient {
private baseUrl: string;
constructor() {
this.baseUrl = EnvironmentConfig.API_BASE_URL;
}
async get<T>(path: string, params?: object): Promise<T> {
const url = `${this.baseUrl}${path}`;
// ... 实际请求实现
return fetch(url, { method: 'GET', headers: {
'X-App-Id': EnvironmentConfig.APP_ID,
'X-Env': EnvironmentConfig.ENV,
} }) as Promise<T>;
}
async post<T>(path: string, body?: object): Promise<T> {
const url = `${this.baseUrl}${path}`;
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Env': EnvironmentConfig.ENV,
},
body: JSON.stringify(body),
}) as Promise<T>;
}
}3.2 功能开关(Feature Flag)
typescript
// common/src/main/ets/config/FeatureFlags.ets
export class FeatureFlags {
/** 开发环境 */
static readonly DEV = {
DEBUG_LOG: true,
MOCK_DATA: true,
NEW_UI: true,
BETA_FEATURES: true,
ANIMATIONS: true,
};
/** 测试环境 */
static readonly BETA = {
DEBUG_LOG: true,
MOCK_DATA: false,
NEW_UI: true,
BETA_FEATURES: true,
ANIMATIONS: true,
};
/** 生产环境 */
static readonly RELEASE = {
DEBUG_LOG: false,
MOCK_DATA: false,
NEW_UI: false,
BETA_FEATURES: false,
ANIMATIONS: true,
};
static get(): typeof this.DEV {
switch (EnvironmentConfig.ENV) {
case 'DEV': return this.DEV;
case 'BETA': return this.BETA;
default: return this.RELEASE;
}
}
}
// 使用
if (FeatureFlags.get().MOCK_DATA) {
const mockUser = { id: 1, name: 'MockUser' };
console.log('使用 Mock 数据:', mockUser);
}4. 多环境构建命令
bash
# 构建开发环境
hvigorw assembleApp --product=dev --mode=DEBUG
# 构建测试环境
hvigorw assembleApp --product=beta --mode=DEBUG
# 构建生产环境
hvigorw assembleApp --product=release --mode=RELEASE
# 仅编译
hvigorw compileApp --product=release5. 环境变量 vs 资源文件方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 编译期 define | 零运行时开销,可 tree-shake | 需重新编译切换 | 大多数场景 |
| 配置类 + 枚举 | 代码可读性好 | 需手动切换 | 快速切换环境 |
| 资源文件 | 运行时切换,无需重新编译 | 需要运行时读取 | 用户可切换环境 |
| 运行时配置 | 最灵活 | 需要网络请求 | 动态配置中心 |
6. 面试高频考点
Q1: 如何在 HarmonyOS 中管理多环境配置?
回答要点:
- 通过
build-profile.json5的products配置定义多环境- 使用 ArkTS
arkFlags注入编译期 define- 运行时通过配置类读取环境变量
- 推荐编译期 define + 配置类结合
Q2: 编译期 define 和运行时配置的区别?
回答要点:
- 编译期 define 在构建时注入,零运行时开销,可 tree-shake 死代码
- 运行时配置更灵活,但不需要重新编译
- 编译期 define 适合 API 地址、环境标识
- 运行时配置适合用户可调节的参数(如主题色)
Q3: 如何管理不同环境的 API 地址?
回答要点:
- 在
build-profile.json5的arkFlags中 define- 通过 EnvironmentConfig 类统一管理
- 使用工厂模式或策略模式切换不同环境
- 注意签名、BundleName 也要随环境变化
7. Android 对比
| 概念 | Android | HarmonyOS |
|---|---|---|
| 多环境 | build.gradle flavorDimensions | build-profile.json5 products |
| BuildConfig | 自动生成的 BuildConfig.java | arkFlags define |
| 签名配置 | signingConfigs (Gradle) | signingConfigs (JSON5) |
| 资源限定符 | res/values-en/ | resources/en_US/ |
| 构建变体 | buildTypes + productFlavors | products + buildModeSet |