第4章:Car App 开发基础

车载应用开发,和手机开发完全是两码事。我刚开始接触 Android Automotive 时,心想不就是个 Android 吗?结果第一个 Demo 跑起来就翻车了——界面被拉伸得不成样子,触摸事件也乱套。嗯,这里面的门道,咱们得好好捋一捋。

4.1 车载应用的生命周期

车载应用的生命周期,说白了就是「车机版 Activity 的一生」。但和手机不同的是,车机上的 Activity 会受到更多系统级事件的干扰。

核心差异点:

  • 手机:用户主动切换 App
  • 车机:系统强制接管(来电、导航、语音)

我习惯把车载 Activity 的生命周期分为三个阶段:

  1. 前台阶段:onCreate → onStart → onResume。和手机一样,但要注意 onResume 可能被频繁触发。
  2. 可见但非交互阶段:onPause。车机里这个状态很常见,比如导航弹窗覆盖了你的 App。
  3. 后台阶段:onStop → onDestroy。系统可能随时杀掉你的进程来释放内存。

避坑指南:我曾经在 onPause 里保存用户输入的数据,结果发现某些车机在低内存时根本不走 onPause,直接杀进程。后来我改用 onSaveInstanceState 配合 Room 数据库,才彻底解决。

4.2 多窗口与分屏适配

车机屏幕越来越大,分屏成了标配。你的 App 必须能在 1/3 屏幕、1/2 屏幕甚至画中画模式下正常工作。

为什么会这样?因为车机系统会强制分屏——左边导航,右边音乐,上面还有状态栏。你的 App 可能被塞进任何一个角落。

4.2.1 尺寸适配策略

屏幕占比 典型场景 适配要点
100% 全屏媒体播放 使用 ConstraintLayout 自适应
50% 分屏音乐+导航 隐藏次要信息,保留核心操作
30% 画中画视频 只显示播放控件,文字尽量少

注意:不要使用固定 dp 值。我见过一个 App 在 10 寸屏上完美运行,换到 12.3 寸屏上按钮直接飞出屏幕。用 match_parent 和 wrap_content 才是正道。

4.2.2 代码示例:分屏适配

// 在 AndroidManifest.xml 中声明
<activity
    android:name=".MainActivity"
    android:resizeableActivity="true"
    android:supportsPictureInPicture="true"
    android:configChanges="screenSize|smallestScreenSize|screenLayout" />

// 监听分屏状态变化
override fun onMultiWindowModeChanged(isInMultiWindowMode: Boolean) {
    super.onMultiWindowModeChanged(isInMultiWindowMode)
    if (isInMultiWindowMode) {
        // 切换到精简布局
        showCompactLayout()
    } else {
        // 恢复全屏布局
        showFullLayout()
    }
}

我个人习惯在 onMultiWindowModeChanged 里做布局切换,而不是在 onCreate 里判断。因为用户可能在行驶中动态调整分屏比例。

4.3 车载 UI 设计规范

车机 UI 和手机 UI 最大的区别是什么?是「安全第一」。你想想看,司机在开车时,视线离开前方的时间不能超过 2 秒。所以我们的 UI 设计必须遵循「一眼看懂,一触即达」的原则。

4.3.1 核心设计原则

  • 大触控区域:按钮至少 48dp × 48dp,推荐 64dp。手指在颠簸的车里很难精准点击。
  • 高对比度:文字和背景的对比度不低于 4.5:1。阳光直射时,低对比度根本看不清。
  • 减少层级:最多两级导航。不要让司机在菜单里翻来翻去。
  • 语音优先:所有操作都要有语音替代方案。

我记得有一次测试,一个 App 的「设置」按钮只有 32dp,测试司机在高速上点了三次都没点中。后来我们改成了 64dp,问题立刻解决。别小看这 16dp 的差距。

4.3.2 车载 UI 组件规范

组件 最小尺寸 间距要求 说明
按钮 48dp × 48dp ≥ 8dp 圆角矩形,避免尖锐边角
列表项 高度 ≥ 56dp ≥ 12dp 支持滑动操作
文本 最小 14sp 行高 1.5 倍 推荐使用系统字体
图标 24dp × 24dp ≥ 16dp 配合文字标签

4.4 安全驾驶模式

安全驾驶模式,说白了就是「开车时别让司机分心」。Android Automotive 提供了两种机制:

  1. 驾驶模式检测:通过车辆 CAN 总线获取车速,判断车辆是否在行驶中。
  2. 内容限制:行驶中禁止显示视频、长文本、复杂表单。

4.4.1 驾驶模式检测实现

// 获取驾驶状态管理器
val car = Car.createCar(context)
val drivingStateManager = car.getCarManager(Car.DRIVING_STATE_SERVICE)

// 监听驾驶状态变化
drivingStateManager?.registerListener(object : DrivingState.OnDrivingStateChangedListener {
    override fun onDrivingStateChanged(state: DrivingState) {
        when (state.status) {
            DrivingState.DRIVING_STATUS_UNRESTRICTED -> {
                // 停车状态,显示全部内容
                showAllContent()
            }
            DrivingState.DRIVING_STATUS_NO_VIDEO -> {
                // 行驶中,隐藏视频
                hideVideoContent()
            }
            DrivingState.DRIVING_STATUS_NO_KEYBOARD -> {
                // 行驶中,禁用键盘输入
                disableKeyboardInput()
            }
        }
    }
})

我曾经踩过一个坑:驾驶状态检测在模拟器上永远返回「停车状态」。后来才发现,模拟器没有 CAN 总线数据,必须用真实车机或模拟器插件才能测试。所以,别在模拟器上验证驾驶模式逻辑。

4.4.2 内容限制策略

根据 Google 的规范,行驶中应该:

  • ✅ 显示导航信息
  • ✅ 显示音乐播放控件
  • ✅ 显示来电信息
  • ❌ 显示视频/动画
  • ❌ 显示长文本列表
  • ❌ 显示设置菜单
  • ❌ 显示键盘输入框

小技巧:我习惯在 ViewModel 里维护一个 isDriving 状态,所有 UI 组件都观察这个状态。这样切换逻辑集中在一处,不会出现某个界面忘记限制的情况。

4.5 本章知识体系

下面这张图总结了本章的核心知识点,你可以把它当作开发时的检查清单:

Car App 开发基础 - 知识体系 应用生命周期 • onCreate → onStart → onResume • onPause(可见但非交互) • onStop → onDestroy(可能被强杀) ⚠ 不要在 onPause 保存关键数据 多窗口与分屏 • 声明 resizeableActivity=true • 监听 onMultiWindowModeChanged • 使用 ConstraintLayout 自适应 ⚠ 避免固定 dp 值 车载 UI 设计规范 • 按钮 ≥ 48dp,推荐 64dp • 对比度 ≥ 4.5:1 • 最多两级导航 ⚠ 语音操作必须覆盖所有功能 安全驾驶模式 • 通过 CAN 总线检测车速 • 行驶中禁止视频/长文本 • 禁用键盘输入 ⚠ 模拟器无法模拟驾驶状态 核心原则:安全第一,简洁至上,适配优先

这四个模块是车载应用开发的基石。你想想看,如果生命周期没处理好,App 可能被系统杀掉;分屏没适配好,界面可能变形;UI 设计不规范,司机可能分心;驾驶模式没实现,可能违反法规。每一个环节都不能掉链子。

总结一下:车载应用开发,本质上是在「功能完整」和「驾驶安全」之间找平衡。我做了这么多项目,最大的体会就是——宁可少一个功能,也不能让司机多看一眼屏幕。

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