第1章 车载设置应用:CarSetting框架、系统设置项管理、用户偏好存储、多用户支持
车载设置应用,说白了就是车机里的“系统设置”。你想想看,用户调节音量、连接蓝牙、切换深色模式,这些操作最终都要落到CarSetting这个应用上。今天我就带你把它拆开看看,到底里面藏着哪些门道。
1.1 CarSetting框架概览
CarSetting不是普通的App。它是Android Automotive系统里一个系统级应用,拥有极高的权限。我刚开始接触它时,第一反应是“这不就是个Settings吗?”后来才发现,它比手机上的Settings复杂得多。
CarSetting的架构大致分三层:
- UI层:基于Fragment的页面结构,每个设置项对应一个Fragment
- 逻辑层:通过CarSettingService管理设置项的读写
- 数据层:直接操作SettingsProvider或CarService
嗯,这里要注意。CarSetting和手机Settings最大的区别在于——它必须依赖CarService。没有CarService,很多车载特有的设置项(比如空调、座椅)根本没法用。
核心要点:CarSetting的入口是CarSettingApplication,它在系统启动时被加载。所有的设置页面通过DashboardFragment统一管理。
我个人习惯把CarSetting的页面结构画成一张树状图。这样一眼就能看出哪些设置项是系统级的,哪些是车载特有的。
1.2 系统设置项管理
系统设置项,说白了就是那些存在数据库里的键值对。Android里用Settings.System、Settings.Global、Settings.Secure来管理。但在车载环境下,事情变得有点不一样。
我记得有一次,客户要求加一个“驾驶模式”的开关。我一开始直接用Settings.System存了个int值。结果发现,用户切换账号后,这个设置居然丢了。为什么?因为Settings.System是跟着用户走的,而驾驶模式应该是全局的。
避坑指南:我曾经踩过这个坑——车载特有的设置项,比如“车辆灯光”、“空调模式”,一定要用Settings.Global或者CarService来存。用Settings.System会导致多用户下数据混乱。
来看一个实际的代码示例。这是我在项目中写的一个设置项管理工具类:
public class CarSettingManager {
private static final String KEY_DRIVING_MODE = "driving_mode";
// 写全局设置
public static void setDrivingMode(Context context, int mode) {
Settings.Global.putInt(
context.getContentResolver(),
KEY_DRIVING_MODE,
mode
);
}
// 读全局设置
public static int getDrivingMode(Context context) {
return Settings.Global.getInt(
context.getContentResolver(),
KEY_DRIVING_MODE,
0 // 默认值
);
}
}
你可能会问,为什么不用SharedPreferences?嗯,这里有个关键点——系统设置项需要跨进程访问。CarSetting是个系统应用,但其他应用(比如语音助手)也可能需要读取驾驶模式。SharedPreferences是进程内存储,根本没法用。
1.3 用户偏好存储
用户偏好,指的是那些跟用户个人习惯相关的设置。比如“座椅记忆位置”、“方向盘力度”、“仪表盘主题”。这些数据的特点是:每个用户都不一样,而且需要持久化。
在Android Automotive里,用户偏好存储主要有三种方式:
| 存储方式 | 适用场景 | 多用户支持 |
|---|---|---|
| Settings.System | 普通用户偏好(音量、亮度) | ✅ 自动隔离 |
| Settings.Global | 全局设置(驾驶模式、车辆配置) | ❌ 所有用户共享 |
| CarService API | 车载硬件偏好(座椅、空调) | ✅ 需手动管理 |
我个人习惯把用户偏好分成两类:UI偏好和硬件偏好。UI偏好比如“列表显示模式”、“排序方式”,这些用Settings.System就够了。硬件偏好比如“座椅位置”,必须通过CarService的API来读写,因为底层涉及到CAN总线通信。
小技巧:如果你需要存储一些复杂的用户偏好(比如JSON格式的配置),可以考虑用Settings.System存字符串。但要注意长度限制,Settings.System的value最大不能超过8192个字符。
1.4 多用户支持
多用户支持,这是车载系统里最容易出问题的地方。你想想看,一辆车可能被家庭里的多个人使用。爸爸开车时座椅调得靠后,妈妈开车时座椅调得靠前。如果系统不区分用户,那每次换人开车都得重新调。
Android Automotive从Android 10开始就内置了多用户支持。每个用户有自己的UserHandle,系统会根据当前用户加载对应的设置项。
我曾经在项目中遇到一个经典问题:用户A设置了蓝牙连接,用户B登录后居然能看到用户A的蓝牙设备列表。这显然是不对的。后来排查发现,蓝牙设备列表存到了Settings.Global里,而不是Settings.System。
核心原则:凡是跟用户个人相关的数据,一定要存到Settings.System或者UserManager管理的存储空间里。全局共享的数据才用Settings.Global。
多用户支持的关键API是UserManager。通过它,你可以获取当前用户的信息,也可以监听用户切换事件:
public class MultiUserHelper {
private UserManager userManager;
public MultiUserHelper(Context context) {
userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
}
// 获取当前用户
public UserHandle getCurrentUser() {
return Process.myUserHandle();
}
// 判断是否是管理员用户
public boolean isAdminUser() {
return userManager.isAdminUser();
}
// 监听用户切换
public void registerUserSwitchListener(Context context) {
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
context.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
// 重新加载当前用户的设置
reloadSettingsForUser(newUserId);
}
}, filter);
}
}
嗯,这里要特别提醒一下。用户切换时,CarSetting应用会被系统重建。所以你在onCreate里一定要重新读取当前用户的设置,不要缓存上一个用户的数据。我见过有同事在静态变量里缓存了用户设置,结果用户切换后界面显示的还是旧数据。
多用户支持还有一个隐藏的坑——存储空间隔离。每个用户有自己的data目录,但系统设置项的数据库是共享的。所以你在读写Settings.System时,系统会自动根据当前用户ID来区分数据。但如果你用了SharedPreferences,那就得自己处理用户隔离了。
避坑指南:我曾经在项目里用SharedPreferences存了用户的“仪表盘布局”配置。结果用户B登录后,读到的还是用户A的布局。后来我改成了用Settings.System存,问题就解决了。记住:多用户环境下,优先用系统设置API,别自己造轮子。
最后总结一下。CarSetting框架的核心就是三件事:页面管理(Fragment)、数据读写(SettingsProvider)、用户隔离(UserManager)。把这三点搞明白了,车载设置应用的基本架构你就掌握了。
在实际开发中,我建议你多看看CarSetting的源码。Google在AOSP里提供了完整的实现,包括DashboardFragment的布局、设置项的注册方式、以及多用户切换的处理逻辑。读懂了源码,你就能避免很多我当年踩过的坑。
公众号:蓝海资料掘金营,微信deep3321