06 · 多 Flavor 与海外适配

一、三 Flavor 是什么(关键澄清)

dcd / xcd / global 不是 debug/release,是两套车机硬件平台 + 海外地区组合。

Flavor平台地区面向
dcdDCD 平台(ro.board.platform == "msmnile"国内国内老一代车机(“watt” 软件分支)
xcdXCD 平台(新一代,非 msmnile)国内国内新一代车机
global复用 XCD 二进制(jar 与 xcd 完全相同)海外海外版(ECE 认证区、欧洲多语言)

源码只有 PLATFORM_MSMNILE = "msmnile"DeviceUtil.java:19),「骁龙 8155」是业界对 msmnile 的通常对应,源码未直接写明型号。 检测:DeviceUtil.isDCDPlatform() = PLATFORM_MSMNILE.equals(getPlatform())

二、平台差异如何屏蔽(编译期 jar 切换,不是源码抽象)

核心机制:dcd 和 xcd 各有一份独立编译的 framework.jar / android.car.jar,编译期按 task 名前置到 bootstrapClasspath

// constants.gradle
dcd_frameworkJarPath = ".../javalib_watt_250721.jar"      // DCD: watt 分支
xcd_frameworkJarPath  = ".../xcd_javalib_1129.jar"        // XCD: xcd 命名前缀
dcd_car_ui_lib_path   = ".../android.car_watt0911.jar"
xcd_car_ui_lib_path   = ".../xcd_android.car_1129.jar"
// dependencies.gradle afterEvaluate:自动给每个 Android 子模块注入 flavor 专属 compileOnly
deps.add("dcdCompileOnly",    files(rootProject.dcd_framework_lib_path))
deps.add("xcdCompileOnly",    files(rootProject.xcd_framework_lib_path))
deps.add("globalCompileOnly", files(rootProject.xcd_framework_lib_path))  // global 复用 xcd
// build.gradle gradle.projectsEvaluated:按 task 名把对应 jar 前置到 bootstrapClasspath
if (currentFlavor.contains("Dcd"))      newFileList.add(new File(DCD_FRAMEWORK_JARPATH))
else if (currentFlavor.contains("Xcd") || currentFlavor.contains("Global"))
                                        newFileList.add(new File(XCD_FRAMEWORK_JARPATH))

约束:源码只用符号常量(如 MiCarPropertyIds.LockCtrl.WALK_AWAY_LOCK),不写魔法数字,不写 if (flavor==dcd)。平台差异写到 src/dcddifsrc/xcddif

三、sourceSet 拼图(源码核实)

app 模块(app/build.gradle

FlavorManifestjavaresaidl
dcdsrc/main/AndroidManifest.xmlmain + dcddifmain + dcddif + region/cnmain + dcddif
xcdsrc/main/AndroidManifest.xmlmain + xcddifmain + xcddif + region/cnmain + xcddif
globalsrc/region/global/AndroidManifest.xml(换文件)main + xcddif + region/globalmain + xcddif + region/globalmain + xcddif

page 模块(以 micarChargeSettings/build.gradle 为准)

Flavorjavares
dcdmain + dcddifmain + dcddif
xcdmain + xcddifmain + xcddif
globalmain + xcddif + globalmain + xcddif + global

命名差异(易混):app 模块用 src/region/global/,page 模块用 src/global/。 当前 app/src/dcddifxcddif 实际只有 res/raw/service_center_provider_info.json 一个文件,没有 Java

四、五种差异化手段(隔离强度从强到弱)

#手段时机隔离粒度示例
1sourceSet 分目录编译期文件级海外专属 Java/资源放 region/global
2globalImplementation 依赖编译期模块级settingsPage/globalOnly 只进 global APK
3BuildConfig.FLAVOR编译期常量if 分支BuildConfig.FLAVOR.contains("dcd") 平台判断
4DeviceUtil.isXxxRegion()运行时if 分支基于 ro.micar.build.region(cn/global/eu/ece)地区判断
5settings:hiddenFeatures运行时单个 PreferenceECE 合规隐藏(HiddenFeaturesKeyListHelper 解析)

编码规范优先级:sourceSet / globalImplementation(编译期)> hiddenFeatures XML 标记 > DeviceUtil / BuildConfig if 分支。

五、globalOnly 模块(海外专属,双重保险)

5.1 限定只在 global 编译

第一道:app 端 globalImplementationapp/build.gradle:168

globalImplementation project(":settingsPage:globalOnly")  // 只进 global APK

第二道:globalOnly 自己 sourceSet 的 dcd/xcd 为空(globalOnly/build.gradle

sourceSets {
    dcd { }    // 空
    xcd { }    // 空
    global { manifest.srcFile 'src/main/AndroidManifest.xml'; java.srcDirs = ['src/main/java']; ... }
}

5.2 内容(来自 settingsPage/globalOnly/readme.md

子包功能
privacy/海外合规隐私(麦克风/摄像头/车辆数据):PrivacySettingsFragment + 20+ Controller
location/海外位置设置:LocationSettingsFragment
security/锁屏 PIN(设置/验证/开机引导):LockPinVerifyActivityLockPinSetActivityProvisionLockPinSetActivity
setupservice/InitialLockSetupService(开机引导锁屏)
bluetooth/BluetoothRequestPermissionActivity(GTS 测试)
common/Google Settings Inject:ExtraSettingsLoader

特点:globalOnly 不依赖 settingsVehicleLib不用 routerApt/voicesearchapt(不走路由/语音体系),只依赖 settingsBaseLib/settingsBaseUi/settingsLibAndroid/settingsLibTile。

六、Manifest 差异(国内 vs 海外)

src/main/AndroidManifest.xml(1875 行)与 src/region/global/AndroidManifest.xml(1904 行)是两份独立完整 Manifest,非增量合并

类别国内(main)海外(global)
国内专属权限mi.car.settings.permission.ATMOSPHERE_LIGHT_SYNCHRONIZE移除
国内专属弹窗强制发电/强制纯电 DialogActivity移除
国内专属 ServiceSettingMisModulePublishService(MIS 车控)移除
海外新增 ActivityLocation/Privacy/Microphone/Camera SettingsActivity
海外新增 ServiceInitialLockSetupService
海外 PIN 流程LockPinVerify/SetActivityProvisionLockPinSetActivity
Activity 主题DarkFullscreenThemeDarkFullscreenThemeKeepStatusBartools:replace

⚠️ 改公共组件必须两边 Manifest 都改

七、首页菜单差异

国内 region/cn/res/xml/miauto_top_level_menu_fragment.xml(14 项带 fragment)vs 海外 region/global/...(16 项):

菜单项国内海外
辅助驾驶AutopilotSettingsFragmentGlobalAutopilotSettingsFragment
位置LocationSettingsFragment
隐私PrivacySettingsFragment

Google 设置项在代码中由 TopLevelMenuFragment.getPreferenceKeyResIdsToRemove() 国内移除,仅海外显示。

八、i18n 资源

values 目录语言
values/默认(英文兜底)
values-zh-rCN/简中
values-en-rGB/英文(海外主语言)
values-de/-fr/-it/-es/-nl/-nb/-sv/欧洲 7 种
  • 欧洲 8 种语言主要在 base/settingsBaseLibbase/settingsBaseUi(齐全)。
  • 不用 RRO(Runtime Resource Overlay),纯 sourceSet 覆盖 + flavor 资源。
  • 翻译流程:开发只改 values/values-zh-rCN/,其他语言由 atlas 平台自动回填。

九、构建产物标识

三 flavor 的 applicationId 相同(com.android.car.settings),靠 Maven publication 区分:

dcd    → com.mi.car:MiCarSettings:<version>-dcd@apk
xcd    → com.mi.car:MiCarSettings:<version>-xcd@apk
global → com.mi.car:MiCarSettings:<version>-global@apk