【BES】系统总览(03):事件如何触发业务处理
上一篇介绍了开机初始化:创建线程、邮箱,初始化按键、蓝牙、音频等模块。
初始化完成后,耳机就可以响应各种变化。例如,用户双击耳机、手机连接成功、App 切换降噪模式,都会触发相应的处理。
这一篇主要介绍:一个事件发生后,是怎样进入业务代码的。
一、事件与业务的区别
事件描述“发生了什么”,业务决定“接下来做什么”。
例如,用户双击左耳:
- 事件:左耳发生双击。
- 业务:根据当前状态,执行播放、暂停或者接听电话等操作。
同一个操作,在不同状态下可能执行不同功能。
整体流程可以这样理解:
%%{init: {"flowchart": {"htmlLabels": true, "wrappingWidth": 1000, "rankSpacing": 30}, "themeVariables": {"lineColor": "#71829a"}}}%%
flowchart TB
subgraph R0["① 事件传递"]
direction LR
R0N0["检测到变化"]
R0N1["传递事件"]
R0N2["找到处理函数"]
R0N0 --> R0N1 --> R0N2
end
class R0N0,R0N1,R0N2 blue;
style R0 fill:#f1f6fd,stroke:#b8cfe8,color:#23456b
subgraph R1["② 业务处理"]
direction LR
R1N0["判断当前状态"]
R1N1["执行具体操作"]
R1N0 --> R1N1
end
class R1N0,R1N1 green;
style R1 fill:#f0f8f3,stroke:#b9d7c4,color:#24563a
R0 --> R1
classDef blue fill:#e8f2ff,stroke:#7ba4d8,color:#23456b,stroke-width:1.5px;
classDef green fill:#e7f4ec,stroke:#78aa8b,color:#24563a,stroke-width:1.5px;
classDef amber fill:#fff3df,stroke:#d5a25b,color:#72501e,stroke-width:1.5px;下面结合 BES 的触摸链路来看。
二、触摸事件如何进入业务处理
1. 将触摸操作变成消息
触摸模块识别出操作后,会调用 capsensor_key_event_process(),传入按键编号和事件类型。

该函数把“哪个按键、发生了什么操作”打包成按键消息,放入 app 邮箱,等待处理。此时还没有决定双击要执行什么功能。
2. app 线程取出消息,交给按键模块
上一篇提到,app_thread 会从邮箱中取消息,再根据模块编号分发。

这条消息的模块编号是 APP_MODULE_KEY,所以会交给按键处理函数 app_key_handle_process()。按键模块初始化时,会先登记由哪个函数处理按键消息;运行时再按登记结果分发。

3. 按键模块进入产品业务
app_key_handle_process() 从消息中还原按键编号和事件类型,然后调用 app_ibrt_user_key()。

app_ibrt_user_key() 会检查当前状态,并决定如何处理按键事件。例如,设备正在充电或耳机在充电仓内时会直接返回;如果当前耳机是从耳,会尝试把按键事件转发给对耳;否则在本机继续处理。
这里的“主从”指 TWS 角色,“左右耳”是按键编号表示的左右身份,是两种不同判断。下面以本机继续处理的分支为例。
4. 区分测试模式和左右耳
本机处理时会进入 app_wgr_ui_handle_key(),这是按键业务的统一入口。

测试模式下,函数处理特定的超长按关机事件;正常模式下,根据 status->code 区分左右耳,并调用对应的按键处理函数。该函数负责分流,不判断双击对应什么功能。
5. 右耳函数根据事件类型分发
下面以右耳为例。

右耳处理函数根据事件类型,分发到单击、双击、三击、长按等对应分支。源码中单击调用已注释,目前不会执行单击功能。
6. 右耳双击根据当前状态执行功能

bt_key_handle_func_doubleclick_right() 会先读取当前通话状态,再决定双击的用途:
- 空闲时:读取右耳双击配置。如果配置为播放/暂停,会继续进入播放处理函数,发送 AVRCP 播放或暂停请求。
- 来电时:接听电话。
- 拨出或通话中:按代码处理挂断。
- 三方通话时:根据当前通话组合处理接听、挂断等操作。
这里可以看出,同一个双击事件会根据耳机当前状态进入不同业务。 产品也可以按需求调整各状态下的处理逻辑。
其他按键事件也是类似流程:先按左右耳和事件类型分发,再结合当前状态执行具体操作。
三、其他事件也可以这样理解
看其他事件时,先关注:事件从哪里来、在哪里接收、由谁分发、最后执行什么业务。
| 场景 | 事件从哪里来 | 业务主要做什么 |
|---|---|---|
| 手机连接耳机 | 蓝牙协议栈上报连接结果 | 更新连接状态,处理后续连接业务 |
| App 切换 ANC | 手机通过 BLE 发送设置命令 | 接收目标模式,进入 ANC 控制流程 |
| 电池电量变化 | 定时采样得到电池电压 | 更新电量,按条件上报或处理低电量 |
这些事件的入口和传递方式不完全相同,但都可以根据事件触发源头,顺着调用链查下去。
四、看业务代码时,怎么找入口
先选一个具体操作,例如“双击暂停音乐”,然后顺着四步查:
- 哪里检测到这个操作?
- 事件通过邮箱还是回调传递?
- 哪个函数判断状态和功能?
- 最后调用什么接口执行?
上一篇主要看“模块怎样初始化”,这一篇看“模块初始化后怎样响应事件”。 先把按键链路看通,再用同样的方法看连接、ANC 和电池等流程。