第1章 车载多屏交互:仪表盘集成、HUD显示、后排娱乐屏、多屏协同

各位同学,今天我们来聊聊车载多屏交互。

说实话,这个题目在Android Automotive开发里,算是比较有挑战的一块。我最早接触这个领域,是在一个量产项目上。当时客户要求仪表盘、中控、HUD三屏联动,我心想这不就是多几个SurfaceView嘛?结果一上手才发现,事情远没那么简单。

1.1 车载多屏的硬件架构

先看硬件层面。车载多屏系统,说白了就是多个显示设备挂在一个或多个SoC上。常见的架构有两种:

  • 单SoC多显示输出:一个芯片驱动多个屏幕,比如高通SA8155,支持最多4路显示输出。
  • 多SoC分布式架构:每个屏幕有自己的处理器,通过以太网或PCIe通信。

我个人习惯用第一种方案,因为开发简单,调试方便。但要注意,单SoC方案对内存带宽要求很高。我在项目中遇到过,仪表盘和中控同时渲染动画时,帧率直接掉到20fps。后来加了硬件合成器才解决。

核心要点:多屏交互的底层依赖显示控制器(Display Controller)和合成器(Composer)。Android Automotive使用HWC(Hardware Composer)来管理多个显示层。

1.2 仪表盘集成

仪表盘是车载系统里最敏感的部分。为什么?因为它直接关系到驾驶安全。

Android Automotive里,仪表盘通常作为一个独立的Activity运行,但它的显示不走常规的WindowManager。嗯,这里要注意:仪表盘需要实时渲染,延迟不能超过16ms。

// 仪表盘Activity的典型配置
<activity
    android:name=".DashboardActivity"
    android:persistableMode="persistAcrossReboots"
    android:configChanges="orientation|screenSize"
    android:excludeFromRecents="true"
    android:taskAffinity="com.android.car.dashboard">
    <meta-data
        android:name="android.car.PRIMARY_DRIVER_DISPLAY"
        android:value="true" />
</activity>

这段代码里,persistableModeexcludeFromRecents是关键。我曾经踩过一个坑:没加persistableMode,结果仪表盘在系统重启后无法自动恢复,用户得手动点开。这在量产车上绝对不行。

个人经验:仪表盘的UI尽量用Canvas或OpenGL直接绘制,别用复杂的布局嵌套。你想想看,一个RelativeLayout套三个LinearLayout,渲染时间直接翻倍。

1.3 HUD显示

HUD(抬头显示)是另一个有意思的东西。它把信息投射到前挡风玻璃上,要求内容简洁、亮度自适应。

Android Automotive里,HUD通常作为一个叠加层(Overlay)存在。它不占用主显示区域,而是通过硬件叠加层直接输出。

// HUD窗口的创建
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.MATCH_PARENT,
    WindowManager.LayoutParams.MATCH_PARENT,
    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
    PixelFormat.TRANSLUCENT);

// 设置HUD的显示区域
params.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
params.y = 50; // 距离顶部50像素

为什么用TYPE_APPLICATION_OVERLAY?因为HUD需要悬浮在其他应用之上,但又不能抢焦点。我记得有一次,HUD窗口不小心设置了FLAG_NOT_TOUCHABLE,结果触摸事件穿透到了仪表盘,导致用户误操作。嗯,这个坑我记了很久。

避坑指南:HUD的亮度调节一定要用系统亮度接口,别自己写PWM控制。我曾经试过直接操作背光驱动,结果在某个车型上导致屏幕闪烁,差点被客户投诉。

1.4 后排娱乐屏

后排娱乐屏相对简单,但有个特殊要求:独立音频输出。前排听导航,后排看电影,互不干扰。

Android Automotive里,后排娱乐屏通常运行一个独立的用户会话(User Session)。每个会话有自己的音频焦点和显示策略。

// 后排娱乐屏的音频策略配置
<audioPolicyConfiguration version="1.0">
    <globalConfiguration speaker_drc_enabled="false">
        <devicePorts>
            <devicePort tagName="Rear Entertainment" type="AUDIO_DEVICE_OUT_BUS" role="source">
                <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                         samplingRates="48000"
                         channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
            </devicePort>
        </devicePorts>
    </globalConfiguration>
</audioPolicyConfiguration>

这段配置定义了一个独立的音频输出设备。我在项目里遇到过一个问题:后排娱乐屏播放视频时,前排的导航语音突然没了。后来发现是音频焦点没处理好。解决方案是给后排娱乐屏分配一个独立的音频流,不参与焦点竞争。

1.5 多屏协同

多屏协同是本章的重点。说白了,就是让多个屏幕像一个整体一样工作。

Android Automotive提供了CarMultiDisplayManager来管理多屏交互。核心功能包括:

  • 跨屏拖拽:用户可以把中控的导航卡片拖到仪表盘上。
  • 内容镜像:后排娱乐屏可以镜像中控的内容。
  • 任务迁移:一个应用可以在不同屏幕间无缝切换。
// 跨屏拖拽的实现
CarMultiDisplayManager multiDisplayManager = 
    (CarMultiDisplayManager) car.getCarManager(Car.MULTI_DISPLAY_SERVICE);

// 注册跨屏拖拽监听
multiDisplayManager.registerDragListener(new CarMultiDisplayManager.DragListener() {
    @Override
    public void onDragStart(int sourceDisplayId, DragEvent event) {
        // 拖拽开始,记录源屏幕
    }

    @Override
    public void onDragEnd(int targetDisplayId, DragEvent event) {
        // 拖拽结束,在目标屏幕显示内容
    }
});

为什么会需要跨屏拖拽?你想想看,用户开车时想把导航信息从仪表盘移到HUD上,如果还要手动操作,那多危险。跨屏拖拽让这一切变得自然。

核心逻辑:多屏协同的本质是显示ID输入事件的映射。每个屏幕有唯一的displayId,系统根据displayId分发触摸事件和渲染指令。

1.6 多屏交互的核心架构图

下面这张图展示了多屏交互的整体架构。我建议你仔细看看,理解每个组件之间的关系。

车载多屏交互核心架构 应用层 仪表盘App | HUD App | 后排娱乐App | 中控App 框架层 CarMultiDisplayManager | WindowManager | AudioManager 系统服务层 SurfaceFlinger | HWC | AudioFlinger | InputDispatcher 硬件层 SoC | 显示控制器 | 音频DSP | 触摸控制器 数据流方向

这张图里,从上到下依次是应用层、框架层、系统服务层和硬件层。多屏交互的核心在框架层和系统服务层。CarMultiDisplayManager负责协调多屏的显示策略,SurfaceFlinger负责实际的合成输出。

1.7 多屏协同的实战要点

最后,我总结几个实战中容易忽略的点:

  1. 显示ID的管理:每个屏幕的displayId是固定的,但不同车型可能不同。建议在代码里用常量定义,别硬编码。
  2. 触摸事件的隔离:后排娱乐屏的触摸事件不能影响到前排。用InputFilter做事件分发过滤。
  3. 性能监控:多屏渲染对GPU压力很大。我建议用dumpsys gfxinfo定期检查帧率。
  4. 电源管理:后排娱乐屏息屏后,要释放对应的显示资源。否则你会发现电池掉得飞快。

小技巧:调试多屏交互时,用adb shell dumpsys display可以查看所有屏幕的状态。包括分辨率、刷新率、是否激活等。这个命令我几乎每天都要用。

好了,这一章的内容就到这里。多屏交互是个大话题,后面我们还会深入每个子系统的实现细节。记住一句话:多屏不是多个单屏的简单叠加,而是一个有机的整体。理解了这个,你就能做出真正好用的车载多屏系统。


公众号:蓝海资料掘金营,微信deep3321