最稳妥的多渠道打包方式是显式声明productFlavors并统一用all闭包注入manifestPlaceholders,避免漏配;buildConfigField用于代码逻辑判断,resValue用于资源引用,敏感字段禁用resValue;外部配置须抽离至properties文件,杜绝硬编码。

直接用 productFlavors + manifestPlaceholders 是最稳妥、最可控的方式,别信“一行脚本全自动生成”的捷径——那些方案要么漏渠道标识,要么改错 BuildConfig 字段,上线后统计全串。
怎么配 productFlavors 才不漏渠道?
必须显式声明每个 flavor 名,并在 all 闭包里统一注入占位符,避免手写时漏掉某个 flavor 的 manifestPlaceholders 配置:
- 错误写法:
productFlavors { xiaomi {} baidu {} }—— 没配manifestPlaceholders,打包后${UMENG_CHANNEL}不会被替换,APK 里留着原始占位符 - 正确写法:
productFlavors.all { flavor -> flavor.manifestPlaceholders = [UMENG_CHANNEL: name] },name自动取 flavor 名,不用手动维护映射 - 如果渠道名含特殊字符(如
_360、appchina),确保AndroidManifest.xml中的占位符名一致:android:value="${UMENG_CHANNEL}",别写成${CHANNEL_VALUE}却在 flavor 里配UMENG_CHANNEL
buildConfigField 和 resValue 怎么选?
两者都生成可读字段,但用途和覆盖范围完全不同:
远程控制 Android 设备,支持截图、自动化操作手机 App 等功能。本应用使用 uiautomator2 作为底层框架,通过 ADB 连接设备,用户无需 Root(1)查看手机屏幕;(2)自动化操作手机 App;(3)远程安装/卸载应用……
-
buildConfigField "String", "API_BASE_URL", '"https://api.baidu.com"'→ 生成在BuildConfig.API_BASE_URL,Java/Kotlin 里直接用,运行时可读,但不能被资源系统(如@string/app_name)引用 -
resValue "string", "app_name", "百度版"→ 生成在R.string.app_name,XML 和代码都能用,但不能用于逻辑判断(比如 if (BuildConfig.DEBUG)) - 严禁在
main/res/values/strings.xml里再定义同名 string,否则会报duplicate value for resource - 敏感字段(如密钥、测试开关)只用
buildConfigField,别塞进resValue——资源文件可能被反编译暴露
为什么 assembleRelease 打不出所有渠道包?
常见原因不是 Gradle 配置错,而是命令没对上 Build Variant:
-
./gradlew assembleRelease只打release类型,但如果你定义了debug和release两种buildTypes,又没显式指定 flavor,它默认只打release对应的 flavor 组合 - 要打全部渠道的 release 包,得用:
./gradlew assembleRelease(前提是 flavor 名已注册且buildTypes.release已启用) - 想单独打某渠道,用:
./gradlew assembleXiaomiRelease(flavor 名首字母大写,buildType 首字母也大写) - 检查是否启用了
android.buildFeatures.buildConfig = true,否则buildConfigField不生效
外部配置文件怎么安全接入?
硬编码在 build.gradle 里等于把渠道密钥写死在 Git 里,必须抽离:
- 建
channel_config.properties放项目根目录,内容如:xiaomi.api_url=https://api.xiaomi.com - 在
build.gradle里读取:def config = new Properties(); config.load(project.rootProject.file("channel_config.properties").newDataInputStream()) - 再传给 flavor:
flavor.buildConfigField "String", "API_URL", "\"${config['xiaomi.api_url']}\"" - 千万别用
System.getenv()或System.getProperty()传敏感值——CI 环境变量容易被日志打印出来 - 本地开发用
local.properties,CI 用加密文件挂载,两者读取逻辑要隔离,避免本地能跑线上炸锅
真正麻烦的从来不是写几行 productFlavors,而是渠道字段散落在 BuildConfig、resValue、manifestPlaceholders、甚至 Java 常量里,改一个漏三个。收敛到单一配置源 + 全自动注入,才是多渠道不翻车的关键。

















