15、Launcher 开发:车载 Launcher 架构、自定义 Launcher、快捷控制中心
车载 Launcher,说白了就是车机系统的「门面」。用户一上车,第一眼看到的就是它。我参与过好几个量产项目,每次 Launcher 的改动都是最频繁的,因为它是用户感知最强的模块。
今天咱们就聊聊车载 Launcher 的架构设计、如何自定义一个 Launcher,以及快捷控制中心(Quick Control Center)的实现思路。
15.1 车载 Launcher 的架构设计
Android Automotive OS 的 Launcher 和手机上的 Launcher 有本质区别。手机 Launcher 主要管应用图标和桌面壁纸,但车载 Launcher 要管的东西多得多——导航卡片、媒体控制、车辆状态、空调调节……
我习惯把车载 Launcher 分成三层:
- 底层(SystemUI 层):处理系统级 UI,比如状态栏、导航栏、锁屏。这部分和 SystemUI 进程绑定。
- 中间层(Launcher 层):负责主界面布局、应用列表、快捷卡片。这是咱们今天重点讨论的。
- 上层(应用层):第三方应用或 OEM 自研应用,通过 Launcher 提供的接口展示内容。
核心要点:车载 Launcher 本质上是一个「聚合器」,它把导航、媒体、车辆信息、应用入口整合到一个界面里。不要把它当成单纯的桌面启动器。
我记得在某个项目里,产品经理要求 Launcher 同时显示导航剩余里程、当前播放歌曲、空调温度、胎压监测……嗯,这已经不是 Launcher 了,这是仪表盘。
15.2 自定义 Launcher 的架构
自定义 Launcher 其实不复杂,但有几个坑要避开。我直接说做法。
15.2.1 基础搭建
首先,你得在 AndroidManifest 里声明 Launcher Activity:
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:theme="@style/Theme.CarLauncher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
注意 launchMode="singleTask",这个很重要。我曾经遇到过用户按 Home 键后 Launcher 被重复创建的问题,加上 singleTask 就解决了。
15.2.2 布局设计
车载屏幕通常是横屏,而且分辨率差异很大(从 720p 到 4K 都有)。我建议用 ConstraintLayout 做根布局,配合百分比约束。
一个典型的车载 Launcher 布局:
- 顶部:状态栏(时间、信号、电量)
- 中间:内容区(导航卡片、媒体卡片、快捷应用)
- 底部:Dock 栏(常用应用、返回 Home 按钮)
- 左侧或右侧:快捷控制中心入口
个人经验:不要把布局写死。用 CarUiRecyclerView 或者自定义 ViewGroup 来做动态布局,方便不同分辨率的适配。我在一个项目里吃过硬编码 dp 的亏,换了屏幕尺寸后整个布局都崩了。
15.2.3 应用列表管理
车载 Launcher 不需要显示所有应用。你想想看,谁会在一辆车上玩《原神》?所以我们要过滤应用。
我一般用 PackageManager 配合 Intent.ACTION_MAIN + Intent.CATEGORY_LAUNCHER 来获取应用列表,然后根据车辆场景做过滤:
fun getCarApps(packageManager: PackageManager): List<ResolveInfo> {
val intent = Intent(Intent.ACTION_MAIN).apply {
addCategory(Intent.CATEGORY_LAUNCHER)
}
val allApps = packageManager.queryIntentActivities(intent, 0)
return allApps.filter { resolveInfo ->
val packageName = resolveInfo.activityInfo.packageName
// 过滤掉非车载应用
!excludedPackages.contains(packageName)
}
}
避坑指南:我曾经在过滤应用时忘了处理 android:enabled="false" 的情况,结果有些被禁用的应用还是显示出来了。记得检查 resolveInfo.activityInfo.enabled。
15.3 快捷控制中心(Quick Control Center)
快捷控制中心是车载 Launcher 的「灵魂」。用户开车时不可能像玩手机一样点来点去,他们需要一键操作。
15.3.1 功能设计
一个标准的快捷控制中心应该包含:
| 功能模块 | 说明 | 实现方式 |
|---|---|---|
| 空调控制 | 温度、风量、模式 | 通过 CarService 的 HVAC 接口 |
| 媒体控制 | 播放/暂停、上一首/下一首 | MediaSession 回调 |
| 导航快捷 | 回家、去公司、搜索 | Intent 调起导航应用 |
| 车辆设置 | 驾驶模式、灯光、门窗 | CarPropertyManager |
| 快捷应用 | 最近使用、常用应用 | PackageManager + 本地存储 |
15.3.2 实现思路
快捷控制中心我建议用 BottomSheet 或者侧滑面板实现。为什么?因为用户操作时不需要离开主界面,体验更连贯。
具体做法:
- 在 Launcher 布局中预留一个 Fragment 或 ViewStub
- 监听用户手势(从边缘滑动或点击按钮)
- 动态加载控制面板的内容
- 每个控制项绑定对应的 CarService API
举个空调控制的例子:
// 获取 HVAC 控制接口
val car = Car.createCar(context)
val hvacManager = car.getCarManager(Car.HVAC_SERVICE) as CarHvacManager
// 设置驾驶员侧温度
hvacManager.setIntProperty(
CarHvacManager.ID_ZONED_TEMP_SETPOINT,
VehicleAreaSeat.SEAT_ROW_1_LEFT,
22 // 22 摄氏度
)
注意:CarService 的 API 在不同 Android 版本上有差异。Android 12 之后引入了 CarPropertyManager,建议用这个统一接口。我手头有个项目还在用 Android 11,两套 API 都得兼容,挺头疼的。
15.4 知识体系总览
下面这张图是我自己总结的 Launcher 开发知识体系,你可以对照着看:
15.5 一些实战建议
最后,分享几个我在项目中踩过的坑:
- 性能问题:车载设备的硬件配置参差不齐。我遇到过用低端芯片跑 4K 屏幕的情况,Launcher 卡成 PPT。解决办法是减少过度绘制,能用
ViewStub就别用View.GONE。 - 多用户问题:Android Automotive 支持多用户(驾驶员、乘客)。Launcher 要区分当前用户,不同用户的快捷控制中心配置不一样。我一开始没注意这个,结果驾驶员 A 的设置被驾驶员 B 改了。
- 安全限制:车辆行驶中,某些操作(比如视频播放、详细设置)要禁用。这个通过
CarOccupantZoneManager判断当前座位和车辆状态来实现。
一个小技巧:调试 Launcher 时,用 adb shell am start -W -n com.example.carlauncher/.MainActivity 可以查看启动耗时。我一般要求 Launcher 冷启动不超过 500ms,热启动不超过 200ms。
好了,Launcher 开发的核心内容就这些。说白了,车载 Launcher 就是一个「聚合器 + 快捷操作面板」,把用户最需要的东西放在最顺手的位置。你想想看,开车的时候谁还有空去翻应用抽屉?