14、通知与状态栏:车载通知规范、状态栏定制、Heads-up 通知

车载系统的通知,和手机完全不是一回事。

我刚开始做 Automotive 项目时,第一反应就是「直接把手机那套通知搬过来不就行了?」结果被测试同事骂得狗血淋头。为什么?因为驾驶员在开车时,视线离开前方超过两秒,风险就急剧上升。手机上的弹窗、横幅、震动,在车里全是安全隐患。

所以这一章,咱们就聊聊车载通知到底该怎么设计、状态栏怎么定制、以及那个让人又爱又恨的 Heads-up 通知。

14.1 车载通知的核心规范

Android Automotive OS 的通知系统,底层还是用 NotificationManager,但上层做了大量限制。说白了,Google 不想让开发者随便往驾驶员的视野里塞东西。

14.1.1 通知优先级与分类

车载通知分三类:

  • 关键通知(Critical):安全相关,比如胎压报警、刹车故障。这类通知会强制弹出 Heads-up。
  • 重要通知(Important):导航提示、来电。会出现在状态栏,但不会强制打断驾驶。
  • 普通通知(Normal):音乐播放、App 更新。只出现在通知中心,状态栏只显示图标。

我见过一个第三方音乐 App,把「每日推荐」设成了关键通知。结果每次启动车辆,驾驶员都被弹窗吓一跳。嗯,这种滥用行为,在车载系统里是要被直接下架的。

14.1.2 通知渠道(Notification Channel)

在 Automotive 上,通知渠道必须提前声明。而且每个渠道都要设置 setImportance(),系统会根据重要性决定是否展示。

// 创建通知渠道示例
NotificationChannel channel = new NotificationChannel(
    "CHANNEL_NAVIGATION",
    "导航提示",
    NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription("用于导航转向、测速等提示");
channel.setBypassDnd(true);  // 允许绕过勿扰模式

NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);

这里有个坑:setBypassDnd(true) 只对关键通知有效。普通通知就算设了,系统也会忽略。我曾经调试了半天,发现通知死活不显示,最后才意识到是重要性等级设错了。

14.2 状态栏定制

车载状态栏和手机完全不同。手机状态栏是给用户看的,车载状态栏是给驾驶员扫一眼的。所以信息密度要低,字体要大,颜色要对比鲜明。

14.2.1 系统状态栏布局

Android Automotive 默认的状态栏分三块:

  • 左侧:时间、日期、温度
  • 中间:通知图标(只显示关键和重要通知)
  • 右侧:系统图标(蓝牙、Wi-Fi、电量、信号)

我参与的一个项目,客户要求把「驾驶模式」图标也放到状态栏。结果发现系统状态栏的图标数量是有限制的,超过 5 个就会自动折叠。最后我们只好把不常用的图标藏到二级菜单里。

14.2.2 自定义状态栏图标

如果你想在状态栏显示自己的图标,比如「行车记录仪正在录制」,可以用 StatusBarManager 的 API。但注意,这需要系统权限。

// 添加自定义状态栏图标(需要系统权限)
StatusBarManager statusBarManager = getSystemService(StatusBarManager.class);
statusBarManager.setIcon("custom_recording", R.drawable.ic_recording, 0, "行车记录仪");

嗯,这里要提醒一下:setIcon() 的第一个参数是 slot 名称,必须先在 config.xml 里定义好。否则系统会直接忽略你的请求。

14.2.3 状态栏的显示策略

车载状态栏还有一个特殊行为:在导航模式下,状态栏会自动隐藏非关键信息。比如音乐播放器的通知图标会被收起来,只保留导航提示和系统状态。

这个逻辑在 SystemUIStatusBar.java 里实现。我记得有一次客户反馈说「状态栏图标闪烁」,查了半天发现是导航 App 和音乐 App 在抢状态栏的显示优先级。最后我们加了一个 isNavigationActive() 的判断才解决。

14.3 Heads-up 通知

Heads-up 通知,就是那个从屏幕顶部弹出来的横幅。在手机上你可能觉得它烦,但在车上,它是救命的功能。

14.3.1 什么时候用 Heads-up

Google 的规定很明确:只有关键通知才能触发 Heads-up。而且 Heads-up 的显示时间不能超过 5 秒,除非用户手动点击。

我见过一个导航 App,每次转弯都弹 Heads-up。结果驾驶员说「我还没看路况呢,弹窗就消失了」。后来我们改成了:第一次弹 5 秒,如果用户没操作,第二次弹 8 秒,第三次直接全屏显示。嗯,这个逻辑后来被 Google 的 CTS 测试给拦下来了——全屏显示属于危险驾驶行为。

14.3.2 Heads-up 的定制

如果你想自定义 Heads-up 的样式,比如加一个「忽略」按钮,可以用 Notification.BuildersetFullScreenIntent()addAction()

// 创建 Heads-up 通知
Notification notification = new Notification.Builder(this, "CHANNEL_CRITICAL")
    .setContentTitle("胎压异常")
    .setContentText("左前轮胎压低于标准值,请检查")
    .setSmallIcon(R.drawable.ic_tire_warning)
    .setPriority(Notification.PRIORITY_MAX)
    .setCategory(Notification.CATEGORY_ALARM)
    .setFullScreenIntent(pendingIntent, true)
    .addAction(R.drawable.ic_ignore, "忽略", ignorePendingIntent)
    .build();

注意 setFullScreenIntent() 的第二个参数是 highPriority,必须设为 true 才能触发 Heads-up。我刚开始做的时候忘了设这个,结果通知只在状态栏闪了一下,根本没弹出来。

14.3.3 Heads-up 的避坑指南

我曾经遇到一个很诡异的问题:Heads-up 通知在模拟器上正常,但在真车上死活不弹。后来发现是车机的「驾驶模式」把通知给拦截了。解决方案是在通知渠道里加上 setBypassDnd(true)

还有一个坑:如果用户正在使用语音助手,Heads-up 通知会被系统自动静音。这是为了防止语音和弹窗同时出现,干扰驾驶员。所以如果你的通知是纯文字的,最好加一个语音播报的 fallback。

14.4 知识体系总览

下面这张图,是我自己整理的车载通知与状态栏的核心逻辑。你可以把它当作一个速查表。

车载通知与状态栏知识体系 通知规范 状态栏定制 Heads-up 通知 关键 / 重要 / 普通 通知渠道声明 重要性等级 左/中/右布局 自定义图标 导航模式隐藏 触发条件 显示时长控制 驾驶模式拦截 核心原则:安全第一,信息精简,不干扰驾驶

14.5 实战建议

最后,给你几个我踩过坑之后总结的建议:

  • 通知渠道一定要提前规划:不要在代码里硬编码渠道名称,用常量类统一管理。否则后期改起来,你会想哭。
  • 状态栏图标不要超过 4 个:超过就会折叠,驾驶员根本看不到。如果必须显示多个,考虑用组合图标。
  • Heads-up 通知一定要加 Action:让驾驶员能快速处理,比如「忽略」或「查看详情」。不要只弹一个「我知道了」的按钮,那等于没弹。
  • 测试时一定要用真车机:模拟器上的通知行为和真车差很多。尤其是驾驶模式、勿扰模式这些,模拟器根本模拟不出来。

核心要点:车载通知不是手机通知的简化版,而是重新设计的系统。关键通知用 Heads-up,重要通知放状态栏,普通通知进中心。状态栏只显示最必要的信息,别让驾驶员分心。

小技巧:如果你不确定某个通知该不该弹 Heads-up,可以问问自己:「如果驾驶员在高速上看到这个通知,会不会吓一跳?」会的话,就别弹。

警告:不要在车载通知里使用震动或声音提醒。除非是安全相关的关键通知,否则一律静音。我见过一个 App 用通知音播放广告,结果被用户投诉到车厂,直接拉黑了。

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