3、车载显示系统:多屏显示架构、主屏与副屏、仪表盘与中控屏的协同
好,我们进入第三章。这一章聊的是车载显示系统的核心——多屏协同。
说实话,我刚入行做IVI那会儿,车上能有一块中控屏就算豪华了。现在呢?主屏、副屏、仪表盘、HUD、后排娱乐屏……五块屏起步。你想想看,这么多屏幕,怎么让它们各司其职,又不打架?这就是我们要解决的问题。
3.1 多屏显示架构:从单屏到多屏的演进
早期的车载系统,说白了就是一块中控屏搞定所有。导航、音乐、设置,全挤在一起。但现在的用户需求变了——副驾要追剧,仪表盘要显示ADAS信息,中控要导航。一块屏根本不够用。
多屏架构,我个人习惯把它分成三种模式:
- 独立模式:每块屏跑自己的应用,互不干扰。比如仪表盘跑仪表应用,中控跑导航。这是最简单的方案。
- 镜像模式:多块屏显示相同内容。比如副屏和中控屏同时显示导航。这个场景其实不多,但有些车厂喜欢这么干。
- 扩展模式:多块屏组成一个逻辑显示区域。比如中控屏和副屏拼接成一个超宽屏,导航跨屏显示。这个对系统要求最高。
我在项目中遇到过一个问题:某款车型要求中控屏和副屏能无缝拖拽应用。听起来很酷对吧?但实现起来,你会发现跨屏的触摸事件传递、焦点管理、窗口层级同步,全是坑。
核心要点:多屏架构的关键在于「显示输出」和「输入事件」的分离。每块屏有自己的显示输出,但输入事件需要统一管理。
3.2 主屏与副屏:谁主谁副?
主屏,通常指中控屏。它是驾驶员最常交互的屏幕。副屏呢?可能是副驾屏,也可能是后排屏。但注意,这里的「主」和「副」不是硬件上的,而是软件上的。
在Android Automotive中,主屏通常拥有最高的窗口管理权限。比如系统对话框、安全警告,必须显示在主屏上。副屏则相对受限,不能覆盖主屏的关键信息。
我建议这样划分职责:
| 屏幕类型 | 典型用途 | 窗口权限 |
|---|---|---|
| 主屏(中控) | 导航、车辆设置、电话 | 全屏、系统级弹窗 |
| 副屏(副驾) | 影音娱乐、游戏 | 全屏,但不可覆盖主屏 |
| 仪表盘 | 车速、ADAS、警示灯 | 固定布局,不可被覆盖 |
嗯,这里要注意:副屏虽然可以全屏显示,但绝对不能遮挡主屏的系统级警告。比如「发动机故障」这种提示,必须让驾驶员第一时间看到。
避坑指南:我曾经在某个项目中,副屏播放视频时占用了整个显示区域,结果系统弹窗被挡住了。用户投诉说看不到倒车影像的警告。后来我们加了一条规则:副屏的窗口层级永远低于主屏的系统窗口。
3.3 仪表盘与中控屏的协同:信息流转的艺术
仪表盘和中控屏,是车载显示系统里最核心的一对。它们的关系,有点像「飞行员和副驾驶」——一个负责关键信息,一个负责辅助信息。
仪表盘显示什么?车速、转速、油量、警示灯。这些是驾驶安全的核心。中控屏显示什么?导航、音乐、空调。这些是舒适和便利性的补充。
但两者不是孤立的。举个例子:导航时,中控屏显示地图,仪表盘显示下一个转弯箭头。这就是协同。
在Android Automotive中,这种协同通常通过两种方式实现:
- SurfaceFlinger的图层合成:仪表盘和中控屏各自有独立的SurfaceFlinger实例,但共享同一个HWC(硬件合成器)。HWC负责把两个屏幕的图层合成到对应的显示设备上。
- 跨进程通信:中控屏的导航应用通过Binder或Socket,把导航指令发送给仪表盘的应用。仪表盘应用再更新自己的显示内容。
我个人更推荐第二种方式。为什么?因为第一种方式对硬件要求高,而且容易出问题。我记得有一次,HWC的图层分配出了bug,导致仪表盘和中控屏的画面互相串了。嗯,那场面,挺尴尬的。
警告:千万不要让仪表盘和中控屏共享同一个SurfaceFlinger实例。除非你确定硬件支持多显示设备的独立图层管理。否则,一个屏幕的卡顿会影响另一个屏幕。
3.4 实战:多屏窗口管理的代码示例
说了这么多理论,来点实际的。下面是一个简单的多屏窗口管理示例,展示如何在中控屏和副屏上分别启动不同的应用。
// 获取显示管理器
DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
// 获取所有显示设备
Display[] displays = displayManager.getDisplays();
// 假设displays[0]是中控屏,displays[1]是副屏
Display primaryDisplay = displays[0];
Display secondaryDisplay = displays[1];
// 在中控屏上启动导航
Intent navIntent = new Intent(context, NavigationActivity.class);
navIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(primaryDisplay.getDisplayId());
context.startActivity(navIntent, options.toBundle());
// 在副屏上启动视频播放
Intent videoIntent = new Intent(context, VideoPlayerActivity.class);
videoIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
ActivityOptions videoOptions = ActivityOptions.makeBasic();
videoOptions.setLaunchDisplayId(secondaryDisplay.getDisplayId());
context.startActivity(videoIntent, videoOptions.toBundle());
这段代码看起来简单,但实际项目中,你还需要处理窗口焦点、触摸事件分发、以及屏幕旋转等问题。比如,副屏上的视频播放器,触摸事件应该只响应副屏的触摸,不能影响到中控屏。
关键点:多屏窗口管理,核心是「显示ID」和「输入设备ID」的映射。每个屏幕有自己的显示ID,每个触摸设备也有自己的ID。系统通过这两个ID,把触摸事件路由到正确的窗口。
3.5 总结与思考
车载多屏显示系统,说白了就是「分而治之,合而协同」。每块屏有自己的职责,但又要能互相配合。作为系统工程师,你需要理解显示架构、窗口管理、事件分发这些底层机制。
最后留个思考题:如果副屏上有一个弹窗,用户点击了「确定」,这个事件应该由谁处理?是副屏自己的应用,还是中控屏的系统服务?嗯,这个问题没有标准答案,但不同的选择会影响用户体验和系统复杂度。
下一章,我们会深入窗口管理器的内部实现,看看Android Automotive是如何管理这些窗口的。到时候,你会对「窗口层级」和「焦点管理」有更深的理解。