鸿蒙应用 多环境打包方案(类似Android变体)

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

鸿蒙系统中,可以通过构建配置变体实现类似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)
环境维度定义productFlavorsproducts配置
差异化资源src/prod/resresourceGroups配置
环境变量注入buildConfigFieldenv字段
签名配置signingConfigsigningConfigs
差异化代码源集(src/prod/java)条件编译(process.env)
构建命令assembleProdReleasehvigor assembleProd
依赖管理prodImplementation通过oh-package管理

六、建议

  1. 环境隔离原则
   // 确保不同环境完全隔离
   "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": {}
    }
  }
}

这种构建可以实现:

  1. 为不同环境生成独立的安装包
  2. 实现代码和资源的差异化编译
  3. 保持相同的代码库管理多个环境
  4. 通过自动化命令简化构建流程
  5. 确保生产环境和测试环境完全隔离

完全符合类似Android变体的需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值