07 - 配置字 CCP 与常驻监听
本篇讲两个”全局级”机制:① 配置字(CCP,Car Config Property) —— 决定”这台车有没有这个功能、支持到什么程度”;②
VehiclePermanentListener—— App 启动时常驻的信号监听器,负责配置字落地和首帧缓存。两者配合解决”打开页面前就能拿到车型能力信息”和”UI 首帧不闪烁”两个问题。
1. 配置字(CCP)是什么
配置字是出厂配置位,描述当前车型的能力。例如:
- 这台车有没有空气悬架?(
SUSPENSION_CONFIG) - 这台车是两驱还是四驱?(
TRANSMISSION_DRIVELINE) - 这台车有没有哨兵模式?
- 这台车支持几个座椅记忆位?
配置字与普通信号的区别:
| 维度 | 配置字(CCP) | 普通车控信号 |
|---|---|---|
| 变化频率 | 几乎不变(车型决定) | 实时变化 |
| 用途 | 决定功能有无/范围 | 决定功能当前状态 |
| 查询入口 | CarConfigManager | SettingsCarPropertyManager |
| 缓存策略 | 强缓存(Int.MIN_VALUE 哨兵) | 按 propId 缓存 |
整机视角(详见整机《车控信号/Android 配置字接入指南》):配置字有三种下发方式——SystemProperty(native
property_get)、Settings.Global(MiCarSettings 用这种)、SOA 信号下发(当普通信号订阅)。本项目的VehiclePermanentListener收到配置字信号后落地到Settings.Global,业务侧再通过CarConfigManager读取。
2. 配置字查询入口:CarConfigManager
路径:base/settingsVehicleLib/src/main/java/com/android/car/settings/miauto/CarConfigManager.kt
- 对业务只暴露
isXxxEnable()形式的方法 - 默认走缓存;动态属性必须
skipCache = true(参考 L619isCarModeInShowRoom、L843getOwnerModeActiveState) - 有单测:
base/settingsVehicleLib/src/test/java/com/android/car/settings/miauto/CarConfigManagerTest.kt
2.1 ⚠️ 哨兵值 Int.MIN_VALUE(关键设计)
CarConfigManager.kt:78 的 getIntProperty 实现用 Int.MIN_VALUE 表示”服务不可用/未取到”:
fun getIntProperty(propId: Int, areaId: Int = 0): Int {
// 拿不到时返回 Int.MIN_VALUE,而非 0 或 -1
}CarConfigManagerTest.kt:97-133 对此做了三次验证。新增 CCP 查询时务必理解:判断”配置字未取到”要用 == Int.MIN_VALUE,不能用 == 0 或 <= 0,否则会把合法的 0 值误判为不可用。
2.2 新增配置字查询的标准动作
- 在
CarConfigManager.kt加isXxxEnable()方法,默认走缓存 - 如果该配置字是动态的(运行时会变),必须
skipCache = true - 用
Int.MIN_VALUE判断服务不可用 - 业务侧调用
CarConfigManager.INSTANCE.isXxxEnable()即可
3. 常驻监听器:VehiclePermanentListener
路径:base/settingsVehicleLib/src/main/java/com/android/car/settings/miauto/VehiclePermanentListener.java
3.1 职责
进程级单例(L87-96),App 启动时常驻,做四件事:
- 订阅一批全局信号:所有 CarConfig 配置字 + 一组业务全局信号(
getPropertyIdSet()L281-309,约 30 个 propId,如驾驶模式、车主模式) - 配置字落地:收到配置字信号时写入
Settings.Global和MiCarSettings.updateConfig(handleConfigPropertyChangedL345-360)——这就是 CCP 运行时落地的实现 - 进程级缓存:维护
mPropertyValueMap(L71),对外暴露静态getCachePropertyValue(propId, areaId)(L335-343)。任何 Controller 在onCreate时即可拿到首帧,避免 UI 闪烁 - 信号拉起 Activity:维护
mPropertyActivityMap/mPropertyPrefKeyMap(L76-77),信号变化时可直接拉起目标 Activity(L401-411)
3.2 启动时机
由 MainThreadStartTask 调用:
app/src/main/java/com/android/car/settings/miauto/MainThreadStartTask.java:62-67
GlobalCarPropertyManager.init(mApplication);
// ...
VehiclePermanentListener.getInstance().init();
MiCarSettings.setConfigManager(CarConfigManager.INSTANCE);这一刻触发 LocalCarManager.createCar,CarService 是否 ready 由系统回调通知。
3.3 二级回调
业务方可注册二级回调监听全局信号:
VehiclePermanentListener.getInstance().addCarPropertyManagerCallback(callback); // L3113.4 给 Controller 的首帧缓存
CarPropertyMgrPreferenceController 在 onCreate 时读 VehiclePermanentListener.getCachePropertyValue(L545)拿到依赖信号初值——这就是”页面还没收到信号上报,但 UI 已经能正确显示”的实现根源。
4. 全局 Manager 入口:GlobalCarPropertyManager
路径:base/settingsVehicleLib/src/main/java/com/android/car/settings/miauto/GlobalCarPropertyManager.kt(L16,object 单例)
- 用
SoftReference持有全局SettingsCarPropertyManager实例(L18,L23-30init) getCarManager()(L32):CarConfigManager等非页面逻辑通过它直接读信号addPropertyCallback(ids: ArraySet<Int>, callback)/removePropertyCallback(L40-59):全局多 callback 监听同一信号集
与
SettingsCarPropertyManager的区别:后者是 per-Controller 实例(每个 Controller 一个,绑页面生命周期);GlobalCarPropertyManager是全局单例,给页面外的逻辑(如MenuAccountController、CarConfigManager)用。
5. 全链路:从车端配置字到 UI 显隐
车端配置字(车型能力)
↓ SOA 信号上报
VehiclePermanentListener 收到配置字信号
├─► 写入 Settings.Global(持久化)
├─► MiCarSettings.updateConfig(内存)
└─► mPropertyValueMap 缓存
↓
CarConfigManager.isXxxEnable() 读 Settings.Global / 缓存
↓
Fragment.getPreferenceKeyResIdsToRemove() // 按配置字移除整个 Preference
Controller.getAvailabilityStatus() // 按配置字控制可用/显隐
6. 关键路径速查
| 主题 | 路径 |
|---|---|
| 配置字查询 | base/settingsVehicleLib/.../miauto/CarConfigManager.kt |
| 配置字单测 | base/settingsVehicleLib/src/test/.../CarConfigManagerTest.kt |
| 常驻监听 | base/settingsVehicleLib/.../miauto/VehiclePermanentListener.java |
| 全局 Manager | base/settingsVehicleLib/.../miauto/GlobalCarPropertyManager.kt |
| App 启动初始化 | app/src/main/.../miauto/MainThreadStartTask.java |
| Settings.Global provider | MiCarSettings(Settings.Global key 定义) |