车载系统定制:SystemUI定制、Launcher开发、系统服务扩展、权限策略定制
说实话,车载系统定制这块,是Android Automotive开发中最有挑战性、也最有意思的部分。你想想看,手机上的Android,Google已经帮你把UI、交互、权限都定义好了。但在车上,每个车厂都有自己的设计语言,有自己的交互逻辑。我当年刚接触这个领域时,第一反应就是——这哪是定制,这简直是重写。
但别怕。今天我就把这四个核心模块拆开揉碎了讲给你听。每个模块我都会结合我踩过的坑、走过的弯路,让你少花点时间在试错上。
SystemUI定制:不只是换个皮肤
SystemUI是什么?说白了,就是状态栏、导航栏、锁屏、通知面板这些系统级UI。在手机上,这些组件基本是固定的。但在车上,情况完全不同。
举个例子。车载状态栏需要显示什么?车速、续航、空调温度、驾驶模式。这些信息手机上有吗?没有。所以你得自己加。
我习惯的做法是,先搞清楚车厂想要哪些信息,然后去SystemUI的源码里找到对应的布局文件。SystemUI的布局主要在packages/SystemUI/res/layout/目录下。比如状态栏,核心文件是status_bar.xml。
核心思路:不要直接改系统源码,而是通过扩展点来定制。SystemUI提供了很多扩展接口,比如StatusBarStateController、NotificationListener。用这些接口,你的代码和系统代码解耦,升级时也省心。
我曾经在一个项目中,车厂要求状态栏显示当前驾驶模式(经济、运动、雪地)。我一开始直接改了status_bar.xml,加了个TextView。结果系统一升级,我的修改全没了。后来我改用StatusBarStateController监听状态变化,再通过addCallback更新UI。嗯,这才是正确姿势。
定制SystemUI时,有几个关键点要注意:
- 布局适配:车载屏幕比例多样,有16:9的,有21:9的,还有异形屏。建议用ConstraintLayout,别用绝对布局。
- 交互方式:车上没有触摸屏?那就要考虑旋钮、语音、方向盘按键。SystemUI的焦点管理要重新设计。
- 性能:车载硬件通常比手机差。SystemUI里的动画、模糊效果,能省则省。我见过一个项目,因为状态栏动画太卡,被客户直接打回。
Launcher开发:桌面就是你的门面
Launcher,也就是桌面。在手机上,Launcher就是放应用图标的地方。但在车上,Launcher是用户和车辆交互的第一入口。
车载Launcher和手机Launcher最大的区别是什么?
手机Launcher是应用启动器。车载Launcher是功能聚合器。用户上车,第一眼看到的不应该是应用图标,而应该是导航、音乐、空调这些核心功能。说白了,车载Launcher更像是一个仪表盘+控制中心的合体。
我开发车载Launcher时,一般会分三层:
- 底层:继承
LauncherActivity或自己实现Activity,管理桌面生命周期。 - 中间层:实现
LauncherAppState,管理应用列表、快捷方式、Widget。 - 上层:UI层,用Fragment或自定义View展示卡片、快捷入口。
这里有个坑。车载Launcher不能像手机那样,让用户随意拖拽图标。为什么?因为驾驶安全。用户应该在最短时间内找到并启动功能。所以,车载Launcher的布局通常是固定的,或者只允许有限的自定义。
我的建议:车载Launcher的核心是「快」。从点击到功能启动,不要超过2秒。如果做不到,就用预加载。我做过一个项目,导航卡片在Launcher启动时就预加载了地图数据,用户点击导航时几乎秒开。
代码层面,Launcher开发的核心是LauncherApps API。这个API可以查询已安装应用、监听应用变化、启动应用。示例代码如下:
// 获取LauncherApps实例
LauncherApps launcherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
// 查询所有应用
List<LauncherActivityInfo> apps = launcherApps.getActivityList(null, userHandle);
// 监听应用变化
launcherApps.registerCallback(new LauncherApps.Callback() {
@Override
public void onPackageRemoved(String packageName, UserHandle user) {
// 移除桌面图标
}
@Override
public void onPackageAdded(String packageName, UserHandle user) {
// 添加桌面图标
}
@Override
public void onPackageChanged(String packageName, UserHandle user) {
// 更新桌面图标
}
});
嗯,代码看起来简单,但实际开发中,你要处理的事情远不止这些。比如,车载Launcher要不要支持分屏?要不要支持悬浮窗?这些都要和车厂反复沟通。
系统服务扩展:给系统加装「外挂」
系统服务,是Android系统的核心。比如WindowManagerService管理窗口,PackageManagerService管理应用。在车载场景下,你需要扩展这些服务,或者干脆自己写一个系统服务。
为什么要扩展系统服务?
举个例子。车厂想要一个「驾驶模式切换」的功能。当用户切换到运动模式时,系统要自动调整UI风格、限制某些应用、改变音量策略。这些逻辑放在哪里?放在App里?不行,因为App可能被杀死。放在SystemUI里?也不行,因为SystemUI只负责UI。正确的做法是,写一个系统服务,常驻内存,统一管理这些逻辑。
我写过不少系统服务,总结下来,步骤就这几步:
- 定义AIDL接口:服务对外暴露的方法,写在AIDL文件里。
- 实现服务类:继承
SystemService,实现AIDL接口。 - 注册服务:在
SystemServer.java中启动服务。 - 添加权限:在
AndroidManifest.xml中声明服务权限。
我曾经在项目中扩展过AudioService,目的是实现「导航音压低媒体音」的功能。原生的AudioService不支持这种策略,所以我加了一个NavigationAudioController,监听导航状态,动态调整音频焦点。嗯,这个功能后来被车厂夸了好几次。
注意:扩展系统服务有风险。一个bug可能导致整个系统崩溃。我建议你在开发时,先用log把每一步都记录下来。出问题时,看日志比猜原因快得多。
权限策略定制:安全与便利的平衡
权限,是Android安全模型的核心。在手机上,权限管理已经很成熟了。但在车上,情况更复杂。
为什么?因为车载系统有多个用户:驾驶员、乘客、访客。每个用户的权限不一样。驾驶员可以访问车辆数据(车速、油耗),乘客只能使用娱乐功能,访客可能连WiFi都不能连。
我做过一个权限策略定制的项目,车厂要求:
- 驾驶员:可以安装应用、访问车辆设置、使用导航。
- 乘客:只能使用预装应用,不能安装新应用。
- 访客:只能使用音乐和视频,不能访问任何车辆数据。
这个需求怎么实现?核心是修改PackageManagerService和PermissionManagerService。
具体来说,我做了三件事:
- 定义角色:在
UserManager中定义三个角色,每个角色对应一组权限。 - 修改权限检查:在
PermissionManagerService中,根据当前用户角色,动态决定是否授予权限。 - 限制应用安装:在
PackageManagerService中,只有驾驶员角色才能调用installPackage。
代码层面,权限检查的核心是checkPermission方法。我重写了这个方法,加入了角色判断:
@Override
public int checkPermission(String permName, String pkgName, int userId) {
// 获取用户角色
int role = getUserRole(userId);
// 根据角色决定权限
if (role == ROLE_DRIVER) {
// 驾驶员:正常检查
return super.checkPermission(permName, pkgName, userId);
} else if (role == ROLE_PASSENGER) {
// 乘客:只允许基本权限
if (isBasicPermission(permName)) {
return PackageManager.PERMISSION_GRANTED;
} else {
return PackageManager.PERMISSION_DENIED;
}
} else {
// 访客:几乎无权限
return PackageManager.PERMISSION_DENIED;
}
}
当然,这只是简化版。实际项目中,你还要考虑权限的继承、撤销、过期等问题。我建议你先把权限策略文档写清楚,再动手写代码。不然,改来改去,很容易出bug。
知识体系总览
说了这么多,你可能有点晕。没关系,我画了一张图,把这四个模块的关系理清楚了。
这张图把四个模块的关系和子项都列出来了。你可以看到,每个模块都有自己的核心任务,但它们又相互关联。比如,权限策略会影响Launcher能显示哪些应用,系统服务会影响SystemUI的行为。
好了,这一章的内容就到这里。车载系统定制是个大话题,每个模块都能单独写一本书。但掌握了这四个核心方向,你就抓住了车载定制的命脉。剩下的,就是在项目中慢慢积累了。