SDL应用生命周期:从启动到退出的完整旅程

每个SDL应用,说白了就是一辆车的「乘客」。它什么时候上车、什么时候下车、在车上做什么,都得遵循一套规矩。这套规矩,就是SDL应用生命周期。

我个人习惯把SDL应用的生命周期分成四个阶段:注册、激活、运行、注销。你想想看,这跟咱们手机上的App其实很像——安装、打开、使用、关闭。

注册阶段:应用与HMI的第一次握手

应用启动后,第一件事就是向SDL Core发送RegisterAppInterface请求。这个请求里包含了应用的基本信息:

{
  "appName": "我的车载音乐",
  "appID": "com.example.mymusic",
  "languageDesired": "ZH-CN",
  "hmiDisplayLanguageDesired": "ZH-CN",
  "appType": ["MEDIA"]
}

嗯,这里要注意:appID必须唯一,否则SDL Core会直接拒绝注册。我在项目中遇到过两次因为appID冲突导致应用无法启动的坑,后来我们团队规定所有appID必须走统一注册流程。

注册成功后,SDL Core会返回RegisterAppInterfaceResponse,里面包含了HMI支持的功能列表。这时候应用才算真正「上车」了。

激活阶段:从后台到前台

注册成功不代表应用就能立刻显示在屏幕上。SDL应用有四种HMI状态:

HMI状态 说明 可执行操作
FULL 全屏显示,用户正在使用 所有UI操作
LIMITED 部分显示,可能被其他应用遮挡 音频播放、有限UI
BACKGROUND 后台运行,不可见 仅数据同步
NONE 未激活,几乎不能做任何事 仅能发送注册请求

应用刚注册完,默认是BACKGROUND状态。要进入FULL状态,需要用户主动点击或语音唤醒。我曾经做过一个导航应用,用户一上车就自动请求FULL状态,结果被HMI拒绝了——因为当时音乐应用正在前台。后来我加了个策略:先监听OnHMIStatus通知,等HMI状态变化了再决定下一步。

运行阶段:RPC请求与响应的交响乐

应用进入FULL状态后,就可以跟HMI进行交互了。SDL的交互方式就是RPC(Remote Procedure Call,远程过程调用)。说白了,就是应用发一个请求,HMI处理完再返回一个响应。

RPC请求的格式很固定:

// 请求示例:显示一个按钮
{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "SDL.Show",
  "params": {
    "mainField1": "欢迎使用车载音乐",
    "softButtons": [
      {
        "type": "TEXT",
        "text": "播放",
        "systemAction": "DEFAULT_ACTION",
        "softButtonID": 100
      }
    ]
  }
}

响应格式也类似:

{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "code": 0,
    "method": "SDL.Show",
    "message": "成功"
  }
}

你可能会问:为什么用JSON-RPC而不是REST?我个人理解,SDL的设计初衷是让应用和HMI之间保持一种「轻量级、低延迟」的通信。JSON-RPC的请求-响应模式天然适合这种场景,而且每个请求都有唯一ID,方便追踪。

避坑指南:我曾经在开发中遇到一个诡异的问题——发送的RPC请求没有收到响应。排查了半天,发现是请求的id字段重复了。SDL Core要求每个请求的id必须递增且唯一,否则会忽略重复id的请求。

SDL UI组件:Button、List、Alert

SDL的UI组件不多,但每个都很实用。我个人觉得,掌握这三个组件就能覆盖90%的车载交互场景。

Button:最基础的交互单元

SDL中的Button分为两种:SoftButton(软按钮)和CustomButton(自定义按钮)。

SoftButton是预定义的,比如「播放」「暂停」「上一首」「下一首」。它们有固定的图标和位置,应用只需要指定按钮ID和文本即可。

CustomButton则灵活得多,可以自定义图标、文本、甚至颜色。但要注意,不是所有HMI都支持CustomButton。我在项目中就遇到过某款车机不支持自定义按钮,只能用SoftButton。

// 创建SoftButton
SoftButton playButton = new SoftButton()
    .setType(SoftButtonType.SBT_TEXT)
    .setText("播放")
    .setSoftButtonID(100)
    .setSystemAction(SystemAction.DEFAULT_ACTION);

// 发送Show请求显示按钮
Show showRequest = new Show()
    .setMainField1("当前歌曲:晴天")
    .setSoftButtons(Arrays.asList(playButton));

List:信息展示的利器

SDL的List组件叫ChoiceSet,它跟手机上的列表不太一样。车载场景下,用户不能像手机那样滑动点击,所以SDL的列表设计成了「语音+按键」的交互方式。

每个列表项都有一个choiceID,用户可以通过语音说出选项内容,或者用旋钮/方向盘按键选择。我刚开始做SDL开发时,总想用列表展示大量数据,结果发现用户根本操作不过来。后来我学乖了:列表项不超过6个,每个选项的文字不超过10个字。

// 创建选择列表
Choice choice1 = new Choice()
    .setChoiceID(1)
    .setMenuName("播放列表");

Choice choice2 = new Choice()
    .setChoiceID(2)
    .setMenuName("搜索歌曲");

// 发送PerformInteraction请求
PerformInteraction pi = new PerformInteraction()
    .setInitialText("请选择操作")
    .setInteractionChoiceSet(Arrays.asList(choice1, choice2))
    .setInteractionMode(InteractionMode.BOTH); // 支持语音和按键

Alert:关键时刻的提醒

Alert是SDL中唯一可以「打断」用户当前操作的组件。比如导航提示、来电提醒、紧急通知等。

Alert有几个关键参数:

  • alertText1/alertText2/alertText3:三行文本,从上到下排列
  • duration:显示时长,单位毫秒
  • softButtons:可以附带按钮,比如「忽略」「查看」
  • playTone:是否播放提示音
注意:Alert不要滥用。我曾经见过一个应用,每次切换歌曲都弹Alert,结果用户直接卸载了。Alert只适合真正需要用户注意的场景,比如导航转弯提示、来电提醒等。

SDL HMI交互:应用与车机的对话

SDL的HMI交互,说白了就是应用和车机屏幕之间的「对话」。这个对话通过SDL Core中转,应用不需要直接操作硬件。

交互流程:三步走

  1. 应用发送RPC请求:比如显示一个按钮、弹出一个Alert
  2. SDL Core转发给HMI:Core会检查请求是否合法,比如按钮ID是否重复、文本是否超长
  3. HMI处理并返回响应:HMI把结果显示在屏幕上,然后告诉Core「处理完了」

这个流程看起来简单,但有个关键点:应用不能直接操作HMI。所有UI操作都必须通过SDL Core。为什么这么设计?我个人的理解是,车载系统对安全性要求极高,不能让每个应用都能直接控制屏幕,万一有个应用崩溃了,整个车机都跟着遭殃。

事件监听:被动接收HMI通知

除了主动发送请求,应用还需要监听HMI发来的事件。比如用户点击了按钮、HMI状态变化了、语音识别结果出来了。

这些事件通过OnXxx系列通知传递:

// 监听按钮点击事件
@Override
public void onOnButtonPress(OnButtonPress notification) {
    int buttonID = notification.getButtonID();
    if (buttonID == 100) {
        // 用户点击了「播放」按钮
        startPlayback();
    }
}

// 监听HMI状态变化
@Override
public void onOnHMIStatus(OnHMIStatus notification) {
    HMILevel level = notification.getHMILevel();
    if (level == HMILevel.HMI_FULL) {
        // 应用进入前台,可以开始交互
        showMainScreen();
    }
}

嗯,这里有个小技巧:不要在事件回调里做耗时操作。SDL的事件回调是在主线程执行的,如果你在里面做网络请求或者数据库操作,会阻塞UI更新。我习惯的做法是:在回调里只做状态标记,真正的业务逻辑放到子线程或者Handler里处理。

交互模式:语音与触控的平衡

车载交互跟手机最大的不同,就是语音优先。用户开车时不能盯着屏幕点来点去,所以SDL的UI组件都支持语音操作。

比如前面提到的ChoiceSet,用户可以直接说「播放列表」来选中对应项。但语音识别不是100%准确的,所以SDL也保留了触控和旋钮操作。我个人建议:所有UI组件都要同时支持语音和触控,不要只依赖一种交互方式。

核心原则:SDL应用开发要始终记住「安全第一」。UI设计要简洁、操作要直观、反馈要及时。不要为了炫酷而牺牲用户体验。

知识体系总览

下面这张图展示了SDL应用开发的核心知识结构,从生命周期到RPC通信,再到UI组件和HMI交互,环环相扣。

SDL应用开发知识体系 应用生命周期 注册 激活 运行 注销 RPC请求与响应 请求格式 响应格式 JSON-RPC ID追踪 SDL UI组件 Button List Alert ChoiceSet HMI交互 三步交互流程 事件监听 语音与触控 SDL应用开发 = 生命周期管理 + RPC通信 + UI组件 + HMI交互

这张图把SDL应用开发的核心知识点串起来了。你想想看,从应用启动到用户交互,每一步都离不开这四个模块的配合。我个人觉得,掌握了这些,SDL基础应用开发就算入门了。

我的建议:刚开始学SDL开发时,不要急着写复杂的功能。先搭一个最简单的应用框架,实现注册-显示按钮-响应点击-注销这个完整流程。跑通了再逐步加功能。我当年就是这么过来的,稳扎稳打比什么都重要。

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