23、WifiMonitor 事件处理:事件分发机制、Supplicant 事件解析、异步处理

WifiMonitor 这个名字,听起来像个监控器,对吧?

其实它的工作就是「监听」。监听谁?监听底层的 wpa_supplicant 发上来的各种事件。我刚开始接触这部分代码时,总觉得它就是个消息中转站,没什么技术含量。后来踩了几个坑才明白——事件分发如果设计不好,整个 Wi-Fi 框架都会乱套。

事件分发机制:谁该处理这个事件?

WifiMonitor 的核心职责,就是把 Supplicant 上报的原始事件,分发给对应的状态机去处理。

你想想看,Wi-Fi 框架里有那么多状态机:

  • ClientModeImpl(客户端模式)
  • SoftApManager(热点模式)
  • P2pStateMachine(P2P 模式)

每个状态机只关心跟自己相关的事件。如果所有事件都往一个地方塞,代码会变成一团乱麻。

WifiMonitor 是怎么做的呢?它内部维护了一个 Handler 注册表。每个状态机启动时,会向 WifiMonitor 注册自己感兴趣的事件类型。事件来了,WifiMonitor 根据事件类型,找到对应的 Handler,把事件丢过去。

核心设计思路:

  • 事件源:wpa_supplicant 通过 socket 上报
  • 事件分发器:WifiMonitor 解析事件类型
  • 事件消费者:各个状态机 Handler

我个人习惯把这种模式叫做「一对多的事件路由」。不是广播,而是精准投递。

Supplicant 事件解析:从字符串到对象

wpa_supplicant 上报的事件,本质上是一行一行的字符串。比如:

CTRL-EVENT-CONNECTED - Connection to 00:11:22:33:44:55 completed [id=1 id_str=]
CTRL-EVENT-DISCONNECTED - bssid=00:11:22:33:44:55 reason=3
WPS-AP-AVAILABLE

这些字符串,WifiMonitor 需要解析成内部的事件对象。解析过程大致分三步:

  1. 识别事件前缀:比如 CTRL-EVENT-CONNECTED 是一个固定前缀
  2. 提取参数:从字符串中抠出 BSSID、reason code、id 等关键信息
  3. 构造事件对象:把解析结果封装成 WifiEvent 或直接发 Message

我在项目中遇到过一个问题:某个定制 ROM 上报的事件格式跟标准不一样,多了一个空格。结果解析失败,Wi-Fi 一直连不上。排查了半天才发现是字符串匹配太死板了。

我的建议:解析 Supplicant 事件时,尽量用 startsWith() 而不是 equals()。因为厂商可能会在事件末尾追加自定义字段。

异步处理:别阻塞主线程

WifiMonitor 的事件处理,必须走异步。为什么?

因为 Supplicant 事件来得很快。连接、断开、扫描结果、P2P 发现……这些事件可能一秒钟来几十个。如果在主线程里同步处理,UI 会卡成 PPT。

WifiMonitor 的做法是:

  • 启动一个独立的 MonitorThread,专门从 socket 读取事件
  • 读取到事件后,通过 Handler 发送到对应的状态机线程
  • 状态机在自己的线程里处理事件,不阻塞 MonitorThread

说白了,就是「生产者-消费者」模型。MonitorThread 是生产者,状态机 Handler 是消费者。

注意:千万不要在 MonitorThread 里做耗时操作。我曾经见过有人直接在事件读取线程里写日志文件,结果 I/O 阻塞导致事件堆积,Wi-Fi 连接超时。嗯,血的教训。

核心流程图:事件从底层到上层

下面这张图,是我自己整理的事件流转路径。你看一遍就能明白 WifiMonitor 在整个链条里的位置。

WifiMonitor 事件分发流程 wpa_supplicant MonitorThread (socket 读取) WifiMonitor 事件解析 ClientModeImpl SoftApManager P2pStateMachine 事件通过 Handler 异步投递,不阻塞 MonitorThread

事件类型分类

Supplicant 事件种类很多,我大致分了四类:

事件类别 典型事件 处理方式
连接事件 CONNECTED, DISCONNECTED 更新连接状态,触发回调
扫描事件 SCAN_RESULTS, SCAN_FAILED 通知扫描管理器
P2P 事件 P2P-DEVICE-FOUND, P2P-GO-NEG-FAILURE 路由到 P2pStateMachine
WPS 事件 WPS-AP-AVAILABLE, WPS-OVERLAP-DETECTED 触发 WPS 相关逻辑

这里有个细节:同一个事件可能被多个消费者关注。比如 DISCONNECTED 事件,ClientModeImpl 要处理,NetworkAgent 也要处理。WifiMonitor 怎么做的?它会把事件同时发给所有注册过的 Handler。

关键点:WifiMonitor 的事件分发是「多播」而不是「单播」。每个 Handler 根据自己的兴趣决定是否处理。

避坑指南:我曾经踩过的坑

讲几个我实际遇到过的问题,希望能帮你少走弯路。

  • 事件丢失:Handler 处理太慢,MonitorThread 那边新事件已经来了。旧事件被覆盖。解决方案是加一个 事件队列,用 BlockingQueue 缓冲。
  • 死锁:状态机在处理事件时,反过来调用了 WifiMonitor 的方法。如果锁没设计好,直接死锁。我建议 状态机内部不要直接调用 WifiMonitor,通过 Message 通信。
  • 事件重复:某些厂商的 wpa_supplicant 会重复上报同一个事件。比如 CONNECTED 事件连发两次。处理时要做 去重,判断当前状态是否已经连接。

一个小技巧:调试 WifiMonitor 时,可以在事件解析处加一个日志开关。平时关闭,出问题时打开。这样既能避免日志刷屏,又能快速定位问题。

总结一下

WifiMonitor 的事件处理,说白了就是三件事:

  • :从 socket 读取 Supplicant 事件
  • :解析字符串,提取关键信息
  • :通过 Handler 异步分发给对应的状态机

结构不复杂,但细节很多。尤其是异步处理和事件去重,稍不注意就会出问题。我个人建议,在写 WifiMonitor 相关代码时,多想想「如果事件来得很快怎么办?」「如果事件顺序乱了怎么办?」。把这些边界情况想清楚,代码质量会提升一大截。


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