14、通知与消息系统:车载通知规范、CarNotification、Heads-up通知、消息优先级管理
通知在手机上,说白了就是个“小红点”或者下拉栏里的一条消息。但在车上,通知直接关系到安全。你想想看,驾驶员正在高速上变道,突然一个全屏弹窗跳出来……那画面太美我不敢看。
所以Android Automotive OS对通知做了大量定制。我刚开始接触车载项目时,以为直接把手机上的通知搬过来就行。结果第一次路测就被安全团队怼了——“你这通知会分散驾驶员注意力”。嗯,从那以后我老老实实啃了一遍车载通知规范。
车载通知的核心原则
车载通知和手机通知最大的区别是什么?两个字:安全。具体来说,有这几个硬性要求:
- 最小化干扰:通知不能遮挡驾驶相关信息(比如导航、车速)。
- 可操作性:驾驶员必须在3秒内理解通知内容并做出决策。
- 优先级分层:只有高优先级通知才能弹出Heads-up,其他乖乖待在通知栏里。
- 语音优先:能语音播报的就不要用文字弹窗。
核心结论:车载通知的设计目标不是“让用户看到”,而是“让用户安全地看到”。
CarNotification:车载通知的专属API
Android Automotive OS提供了一个专门的API——CarNotification。它和手机上的NotificationCompat很像,但多了几个车载特有的参数。
我个人习惯在构建车载通知时,直接使用CarNotification.Builder。来看一个实际例子:
// 构建一个车载通知
CarNotification.Builder builder = new CarNotification.Builder(context, CHANNEL_ID)
.setContentTitle("充电完成")
.setContentText("您的车辆已充满电,可以出发了")
.setSmallIcon(R.drawable.ic_ev_charge_done)
.setPriority(CarNotification.PRIORITY_HIGH)
.setCategory(CarNotification.CATEGORY_CHARGING)
.setHeadsUp(true) // 允许Heads-up弹出
.setDuration(5000) // 持续5秒后自动消失
.addAction(new CarNotification.Action.Builder(
R.drawable.ic_unplug,
"停止充电",
pendingIntent
).build());
// 别忘了设置汽车专属的元数据
builder.setCarMetadata(new CarNotification.CarMetadata.Builder()
.setSupportsVoiceReply(true)
.setRequiresDriverAttention(false)
.build());
notificationManager.notify(TAG, NOTIFICATION_ID, builder.build());
这里有个坑,我曾经踩过:setHeadsUp(true)并不保证一定会弹出Heads-up。系统还会根据当前驾驶状态、通知优先级、以及用户设置来决定是否真的弹出。说白了,你只是“申请”弹出,系统才是最终裁判。
Heads-up通知:什么时候该用?
Heads-up通知就是那种从顶部滑下来的半透明横幅。在车上,它比全屏弹窗安全得多,但也不能滥用。
根据Android Automotive的官方建议,只有以下场景才适合使用Heads-up:
- 安全警告:比如“前方事故,请减速”
- 车辆状态变化:比如“胎压过低”
- 导航指令:比如“前方500米右转”
- 来电:尤其是通过蓝牙连接的来电
其他通知,比如“天气更新”、“应用推荐”,老老实实待在通知栏里就好。你想想看,驾驶员正在找停车位,突然弹出一条“今天下午有雨”……这不是添乱吗?
我的经验:在开发阶段,可以用adb shell dumpsys notification查看当前通知的优先级和Heads-up状态。如果发现你的通知没有按预期弹出,先检查一下优先级是否设置正确。
消息优先级管理:谁该先说话?
车载通知的优先级分为四个等级,从高到低依次是:
| 优先级 | 常量 | 典型场景 | 是否允许Heads-up |
|---|---|---|---|
| 紧急 | PRIORITY_CRITICAL |
碰撞预警、刹车故障 | 是(强制弹出) |
| 高 | PRIORITY_HIGH |
导航指令、来电 | 是(需系统批准) |
| 中 | PRIORITY_DEFAULT |
充电完成、媒体播放 | 否 |
| 低 | PRIORITY_LOW |
应用更新、订阅提醒 | 否 |
这里要注意的是,PRIORITY_CRITICAL是车载特有的,手机上没有。它可以让通知绕过“免打扰”模式,直接显示在屏幕上。但滥用这个优先级会被系统标记为“恶意应用”,甚至被用户卸载。我曾经见过一个第三方导航应用,把所有通知都设成CRITICAL,结果被车主投诉到应用商店下架了。
通知分组与折叠
在车上,通知栏的空间比手机更宝贵。所以Android Automotive OS默认会对同一应用的通知进行分组。比如你收到了5条微信消息,系统会折叠成一条“微信(5条消息)”。
要实现分组,你需要在构建通知时设置setGroup():
// 同一组通知使用相同的groupKey
String groupKey = "group_wechat";
CarNotification.Builder builder = new CarNotification.Builder(context, CHANNEL_ID)
.setGroup(groupKey)
.setGroupSummary(true) // 这条作为组摘要
.setContentTitle("微信")
.setContentText("您有5条未读消息")
.setSortKey(String.valueOf(System.currentTimeMillis()));
分组后的通知在车载UI上会显示为一个可展开的卡片。驾驶员可以语音命令“展开微信通知”来查看详情,而不需要动手操作。
驾驶模式下的通知行为
当车辆处于行驶状态时,系统会自动进入“驾驶模式”。在这个模式下,通知行为会发生以下变化:
- 所有Heads-up通知的显示时间缩短到3秒(静止状态下是5秒)
- 低优先级通知直接被静音,不显示也不震动
- 通知中的操作按钮会被限制,只保留语音相关的操作
- 文字内容超过两行会被截断,只显示“查看更多”提示
我记得有一次测试,发现我们的通知在驾驶模式下按钮全部消失了。查了半天,原来是没有设置setSupportsVoiceReply(true)。系统认为这个通知无法通过语音操作,干脆把按钮全砍了。嗯,这个坑大家注意一下。
知识体系总览
下面这张图总结了车载通知系统的核心逻辑和决策流程:
从这张图可以看出,一个通知从诞生到展示给驾驶员,经历了优先级判断、Heads-up申请、驾驶模式过滤三道关卡。每一关都可能改变通知的最终形态。
避坑指南
最后分享几个我实际项目中踩过的坑:
- 不要依赖通知的默认优先级:如果不显式设置优先级,系统会默认为
PRIORITY_DEFAULT。这意味着你的通知永远不会弹出Heads-up。我曾经有一个充电提醒功能,用户反馈“为什么插上充电器没反应?”——查了半天,就是忘了设优先级。 - Heads-up的持续时间要合理:太短用户看不到,太长影响驾驶。我一般设3-5秒,具体看通知内容长度。
- 语音回复的坑:如果通知支持语音回复,一定要在
CarMetadata中设置setSupportsVoiceReply(true)。否则系统会认为这个通知“不可操作”,直接砍掉所有按钮。 - 测试时模拟驾驶模式:用
adb shell settings put global automotive_driving_state 1可以强制进入驾驶模式。我每次提交代码前都会跑一遍这个测试,确保通知在驾驶模式下表现正常。
重要提醒:不要在通知中显示需要驾驶员手动输入的内容,比如验证码、密码等。车载系统严禁任何形式的文本输入操作。如果必须输入,请使用语音或预设选项。
好了,关于车载通知的内容就讲到这里。记住一句话:在车上,通知不是用来“看”的,而是用来“安全地获取信息”的。把握好这个原则,你的车载通知设计就不会跑偏。