1. uni-app Android 离线打包环境配置实战
从事跨平台开发五年多,我发现很多团队在uni-app离线打包环节都存在环境切换的痛点。上周刚帮一个电商项目解决了多环境打包问题,他们的测试组每天要打十几个包验证不同功能模块,手动改配置效率极低还容易出错。下面分享我们最终采用的标准化方案。
离线打包的核心价值在于突破HBuilderX的限制,可以直接在Android Studio中调试原生功能。但官方文档对多环境配置的说明比较分散,新手容易踩坑。通过本文你将掌握:
- 如何建立prod/dev双环境配置体系
- 一套配置多处复用的技巧
- 打包时自动识别环境的实现方案
关键提示:所有操作基于uni-app 3.7.9+和Android Studio Giraffe版本验证,建议先统一开发环境
1.1 基础工程结构改造
首先在原生工程中建立环境隔离体系。打开Android Studio中的app模块,在src目录下新建两个文件夹:
src/ ├── dev/ │ ├── assets/ │ └── res/ └── prod/ ├── assets/ └── res/关键文件配置逻辑:
data/dcloud_control.xml- 应用标识文件assets/apps/[appid]/www/- 前端编译产物res/values/strings.xml- 环境相关变量
避坑指南:不要直接修改main下的资源文件,会导致环境切换失效。我们吃过亏,有次紧急上线打成了测试环境配置。
1.2 多环境资源配置方案
在dev/res/values和prod/res/values中分别创建环境专属配置:
<!-- dev环境示例 --> <string name="app_name">MyApp(Dev)</string> <string name="api_base">https://dev.api.example.com</string> <bool name="debug_mode">true</bool> <!-- prod环境示例 --> <string name="app_name">MyApp</string> <string name="api_base">https://api.example.com</string> <bool name="debug_mode">false</bool>通过Build Variants实现环境切换:
- 打开
build.gradle文件 - 在android块内添加以下配置:
flavorDimensions "environment" productFlavors { dev { dimension "environment" applicationIdSuffix ".dev" manifestPlaceholders = [ APP_NAME: "@string/app_name_dev" ] } prod { dimension "environment" manifestPlaceholders = [ APP_NAME: "@string/app_name" ] } }2. uni-app与原生环境联调方案
2.1 前端代码环境适配
在uni-app项目中创建环境配置文件env.js:
// 开发环境配置 const dev = { baseUrl: 'https://dev.api.example.com', debug: true, // ...其他配置 } // 生产环境配置 const prod = { baseUrl: 'https://api.example.com', debug: false, // ...其他配置 } // 根据打包参数自动选择环境 export default process.env.NODE_ENV === 'development' ? dev : prod在manifest.json中配置环境变量注入:
{ "name": "MyApp", "appid": "__UNI__XXXXXX", "description": "", "versionName": "1.0.0", "versionCode": "100", "transformPx": false, "uni-app": { "scripts": { "dev": { "title": "开发环境", "env": { "UNI_PLATFORM": "app-plus", "NODE_ENV": "development" } }, "prod": { "title": "生产环境", "env": { "UNI_PLATFORM": "app-plus", "NODE_ENV": "production" } } } } }2.2 原生模块环境感知
在Android原生代码中获取当前环境配置:
public class EnvHelper { public static boolean isDevMode(Context context) { try { return context.getResources().getBoolean(R.bool.debug_mode); } catch (Exception e) { return false; } } public static String getApiBase(Context context) { return context.getString(R.string.api_base); } }使用示例:
if (EnvHelper.isDevMode(this)) { // 开发环境特殊逻辑 Log.d("TAG", "当前运行在开发环境"); }3. 完整打包流程实现
3.1 前端资源编译
使用HBuilderX或命令行编译不同环境的前端资源:
# 开发环境 npm run dev:app-plus # 生产环境 npm run build:app-plus编译完成后,将unpackage/dist/build/app-plus下的文件分别拷贝到Android工程的对应目录:
- dev环境:
src/dev/assets/apps/[appid]/www/ - prod环境:
src/prod/assets/apps/[appid]/www/
3.2 Gradle打包配置优化
在app/build.gradle中添加资源过滤规则:
android { sourceSets { dev { assets.srcDirs = ['src/dev/assets'] res.srcDirs = ['src/dev/res'] } prod { assets.srcDirs = ['src/prod/assets'] res.srcDirs = ['src/prod/res'] } } }添加打包任务别名:
task assembleDev(type: Assemble, dependsOn: assembleDevRelease) { group = 'build' description = '打包开发环境Release版本' } task assembleProd(type: Assemble, dependsOn: assembleProdRelease) { group = 'build' description = '打包生产环境Release版本' }3.3 一键打包脚本
创建package.sh自动化脚本:
#!/bin/bash # 参数检查 if [ $# -ne 1 ]; then echo "Usage: $0 [dev|prod]" exit 1 fi ENV=$1 # 编译前端资源 echo "正在编译${ENV}环境前端资源..." if [ "$ENV" == "dev" ]; then npm run dev:app-plus else npm run build:app-plus fi # 拷贝资源文件 echo "拷贝资源到Android工程..." APP_ID=$(cat src/main/assets/data/dcloud_control.xml | grep appid | awk -F'"' '{print $4}') DST_DIR="src/${ENV}/assets/apps/${APP_ID}/www" rm -rf "${DST_DIR}" mkdir -p "${DST_DIR}" cp -r unpackage/dist/build/app-plus/* "${DST_DIR}" # 执行打包 echo "开始打包${ENV}版本..." if [ "$ENV" == "dev" ]; then ./gradlew assembleDev else ./gradlew assembleProd fi echo "打包完成!输出目录:app/build/outputs/apk/${ENV}/release/"4. 常见问题排查指南
4.1 环境切换失效问题
现象:修改gradle配置后环境变量未生效
排查步骤:
- 检查Build Variants是否选对(View -> Tool Windows -> Build Variants)
- 确认
app/build/generated/source/buildConfig下是否有对应环境的配置类 - 清理工程后重新编译(File -> Invalidate Caches)
解决方案:
// 在build.gradle中添加 android { defaultConfig { // 确保每次修改环境配置后版本号变化 versionCode System.currentTimeSeconds() / 60 } }4.2 资源文件冲突问题
现象:部分图片或布局文件在不同环境表现不一致
根本原因:Android资源合并策略导致
最佳实践:
- 公共资源放在
main/res目录 - 环境特有资源放在各自环境目录
- 使用资源前缀避免命名冲突:
<!-- dev/res/values/strings.xml --> <string name="dev_app_name">MyApp Dev</string> <!-- prod/res/values/strings.xml --> <string name="prod_app_name">MyApp</string>4.3 包名冲突问题
现象:同一设备无法同时安装dev和prod版本
解决方案:
productFlavors { dev { applicationId "com.example.myapp.dev" } prod { applicationId "com.example.myapp" } }5. 高级配置技巧
5.1 动态加载第三方SDK
根据不同环境初始化不同配置:
public class SDKManager { public static void init(Context context) { if (EnvHelper.isDevMode(context)) { // 测试环境SDK配置 MobSDK.init(context, "dev_appkey", "dev_secret"); } else { // 正式环境SDK配置 MobSDK.init(context, "prod_appkey", "prod_secret"); } } }5.2 环境专属功能开关
在build.gradle中定义环境变量:
productFlavors { dev { buildConfigField "boolean", "ENABLE_TEST_FEATURE", "true" } prod { buildConfigField "boolean", "ENABLE_TEST_FEATURE", "false" } }代码中使用:
if (BuildConfig.ENABLE_TEST_FEATURE) { // 仅开发环境可见的功能 }5.3 自动化构建集成
Jenkins pipeline示例:
pipeline { agent any parameters { choice( name: 'BUILD_ENV', choices: ['dev', 'prod'], description: '选择构建环境' ) } stages { stage('Checkout') { steps { git branch: 'main', url: 'git@example.com:repo.git' } } stage('Build') { steps { script { if (params.BUILD_ENV == 'dev') { sh './package.sh dev' } else { sh './package.sh prod' } } } } stage('Deploy') { when { expression { params.BUILD_ENV == 'prod' } } steps { // 生产环境部署逻辑 } } } }这套方案在我们团队已经稳定运行两年多,支持了20+应用的持续交付。最大的收益是彻底消除了人工配置错误导致的线上事故,打包效率提升了70%。最近我们还扩展了staging环境支持,通过jenkins参数化构建实现了一键生成任意环境包体。