如何为不同渠道(如华为、小米、应用宝)生成不同的包名和资源?

解读

国内 Android 生态高度碎片化,各大商店对包名、渠道号、图标、闪屏、支付 SDK、推送 SDK 甚至隐私文案都有“强校验”要求。面试官问“不同渠道不同包名+资源”,核心想确认三件事:

  1. 你是否理解 Gradle 构建模型(buildType × productFlavor × variant)
  2. 你是否能把“包名”与“applicationId”解耦,并知道在 Manifest 里哪些地方必须同步改动(如 provider、activity 的 authorities、deepLink scheme)
  3. 你是否能在 CI 阶段一次性打出 20+ 渠道包,且保证 R 文件、资源合并、签名、对齐、加固、QA 可追溯

一句话:不是“能不能改”,而是“怎么改得又快又稳还能回滚”。

知识点

  1. Gradle 3.x+ 的 productFlavor 维度化(dimension)与 flavorDimensions 顺序决定资源覆盖优先级
  2. applicationIdSuffix / applicationId 与 Manifest package 属性解耦;provider authorities 必须动态替换,否则安装时会出现 “Provider class not found” 或 “INSTALL_FAILED_CONFLICTING_PROVIDER”
  3. manifestPlaceholders + buildConfigField + resValue 三件套,分别注入到 Manifest、Java/Kotlin、资源(xml/资源值)
  4. sourceSet 叠加顺序:src/main → src/flavor → src/buildType → src/flavorBuildType;drawable/mipmap/values 同名即覆盖
  5. 国内特殊限制:
    • 华为强制要求“包名+.huawei”后缀才给 push 权限
    • 小米应用商店会扫描 apk 内 assets/channel 文件,值必须与后台登记一致
    • 应用宝对 V1+V2 签名同时校验,且对加固后二次签名有白名单
  6. 资源合并冲突策略:gradle 4.0+ 采用 “strict” 模式,重复资源会中断构建,需使用 tools:node="remove" 或 flavor 限定
  7. CI 并发构建注意:不同 applicationId 的 R 路径不同,需关闭 gradle 缓存或按 variant 隔离 buildDir,否则出现类重复
  8. 回滚方案:在 Google Play 使用 applicationId 作为唯一标识,国内渠道一旦发版即不可修改,因此脚本里必须保存 mapping.txt、nativesymbols、R.txt、三方 SDK 的 appKey 清单,方便 hotfix 用同一 applicationId 出补丁包

答案

步骤化落地方案(可直接写进简历“多渠道构建”条目):

  1. 在 app/build.gradle 声明维度与风味
flavorDimensions "vendor"
productFlavors {
    create("huawei") {
        dimension "vendor"
        applicationId "com.demo.app.huawei"
        manifestPlaceholders["AUTHORITY"] = "${applicationId}.fileprovider"
        buildConfigField "String", "CHANNEL", "\"huawei\""
        resValue "string", "app_name", "Demo-华为"
    }
    create("xiaomi") {
        dimension "vendor"
        applicationId "com.demo.app.mi"
        manifestPlaceholders["AUTHORITY"] = "${applicationId}.fileprovider"
        buildConfigField "String", "CHANNEL", "\"xiaomi\""
        resValue "string", "app_name", "Demo-小米"
    }
    create("yingyongbao") {
        dimension "vendor"
        applicationId "com.demo.app.qq"
        manifestPlaceholders["AUTHORITY"] = "${applicationId}.fileprovider"
        buildConfigField "String", "CHANNEL", "\"yingyongbao\""
        resValue "string", "app_name", "Demo-应用宝"
    }
}
  1. 在 AndroidManifest.xml 中动态替换 authorities
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${AUTHORITY}"
    android:exported="false"
    android:grantUriPermissions="true">
    ...
</provider>
  1. 资源隔离
    建立目录
    src/huawei/res/mipmap-hdpi/ic_launcher.png
    src/xiaomi/res/values/strings.xml(内含 app_name、umeng_key、push_key)
    src/yingyongbao/res/values/themes.xml(闪屏背景色)
    gradle 会自动按优先级覆盖,无需手动 merge

  2. 一键打 3 个包

./gradlew assembleRelease

输出
app-huawei-release.apk
app-xiaomi-release.apk
app-yingyongbao-release.apk
各自拥有独立包名、签名、资源、渠道字段

  1. CI 加固与校验
    在 GitLab CI 中并行跑
stages:
  - build
  - security
build:
  script:
    - ./gradlew assembleRelease
  artifacts:
    paths:
      - app/build/outputs/apk/*/*.apk
security:
  script:
    - python scripts/verify_apk.py --apk app/build/outputs/apk/huawei/release/app-huawei-release.apk --expect-md5 xxxx
    - /usr/local/360jiagu/jiagu -login user pass
    - /usr/local/360jiagu/jiagu -jiagu app-huawei-release.apk -autosign

保证加固后仍使用原 applicationId,且二次签名 V1+V2 完整

  1. 热修复回滚
    Tinker 补丁工具读取 mapping.txt 时,通过 flavor 名定位到
    build/outputs/mapping/huaweiRelease/mapping.txt
    确保补丁包只针对对应渠道,不会出现 “unexpected dex difference” 错误

拓展思考

  1. 如果渠道数膨胀到 50+(Oppo、Vivo、三星、魅族、运营商、企业定制),维护 50 个 flavor 会导致配置爆炸,此时可引入“动态 flavor”:在 gradle 配置阶段读取 channel.txt 文件,使用 Groovy 闭包动态 create(),并把公共资源抽成 com.android.library 模块,实现“千人千包”而脚本长度不变
  2. 包名后缀方案虽然简单,却会让同一设备出现“平行安装”现象,若业务要求“唯一安装”,则需放弃 applicationIdSuffix,改用“包名不变+渠道号在 metadata”方案,但华为推送、小米推送又强制包名校验,此时只能走“多进程+推送多实例”或“统一推送联盟”方案,面试时可展示你对统一推送联盟(UPS)的集成经验
  3. Google 已强制 AAB,国内商店逐步跟进。AAB 的多渠道思路不再是“本地打出 50 个 APK”,而是“上传一个 AAB + 50 个渠道配置文件”,由商店端按规则重新签名、生成独立 APK。此时需把资源差异化拆分成“asset-pack”或“configuration-apk”,并学会使用 bundletool build-apks --device-spec 来本地验证,提前储备 AAB 时代下的“分包名”能力,才能在下一轮面试继续领先