4. 事件类型与触发机制:六大事件源驱动状态流转
在状态机驱动的埋点生命周期中,事件是触发状态跃迁的唯一输入。每类事件携带特有的语义、触发时机和载荷结构,状态机根据事件类型执行预定义的转换规则,同时记录埋点生命周期中的关键节点。本章定义六大标准事件类型,并给出触发机制、数据结构与 SDK 注册示例。
💡 核心原则:事件是状态机的燃料。每一个埋点实例从创建到销毁,其状态变化均由事件驱动。事件在生命周期中的到达 순서、频率、参数必须可追溯。
4.1 事件类型总览与分类
| 事件类别 | 触发时机 | 典型载荷字段 | 状态机响应动作 |
|---|---|---|---|
| 应用启动 | 进程冷启动 / 热启动 | launch_type, session_id, timestamp |
初始化状态机、重置埋点计数器 |
| 页面进入 | 路由跳转完成、Fragment/Activity 可见 | page_name, referrer, page_id |
设置当前页面上下文、触发 PV 埋点 |
| 用户操作 | 点击、长按、滑动、输入聚焦等 | element_id, action_type, extra_params |
校验操作合法性、触发行为埋点 |
| 网络变化 | WiFi 连接 / 断开、蜂窝数据切换 | network_type, is_connected |
更新网络状态、重试失败队列 |
| 超时 | 定时器到期(会话超时、请求超时) | timeout_type, duration, context |
回退 / 重置状态、触发超时埋点 |
| 错误事件 | 崩溃、异常捕获、API 响应错误 | error_code, stack_trace, severity |
进入错误处理子状态、记录错误快照 |
| 业务自定义事件 | 业务逻辑特定时机(支付成功、订单变更) | event_name, properties, business_id |
调用注册的扩展处理器、透传上下文 |
4.2 触发机制与状态机路由
所有事件统一经过 事件路由器,路由器根据事件类型将事件分发到对应的处理器。处理器内部维护一个 事件-状态转换表,确保只有满足前置条件的事件才会触发状态变化。
路由规则示例(YAML 配置):
event_routing:
- event_type: "app_start"
handler: "AppStartHandler"
allowed_states: ["IDLE", "SUSPENDED"]
priority: 1
- event_type: "page_enter"
handler: "PageEnterHandler"
allowed_states: ["ACTIVE", "FOREGROUND"]
priority: 2
- event_type: "user_action"
handler: "UserActionHandler"
allowed_states: ["ACTIVE", "PAGE_FOCUSED"]
priority: 3
- event_type: "network_change"
handler: "NetworkChangeHandler"
allowed_states: "*"
priority: 10
- event_type: "timeout"
handler: "TimeoutHandler"
allowed_states: ["ACTIVE", "WAITING", "SUSPENDED"]
priority: 5
- event_type: "error"
handler: "ErrorHandler"
allowed_states: "*"
priority: 0
- event_type: "custom"
handler: "CustomEventHandler"
allowed_states: "*"
priority: 100
4.3 SDK 事件注册与派发 API
埋点 SDK 提供统一的 trackEvent 接口,调用方只需传入事件类型和负载。SDK 内部自动完成类型校验、绑定上下文、推入队列并通知状态机。
// TypeScript SDK 示例
import { EventBus, EventType } from '@track/state-machine-sdk';
// 注册监听器(内部状态机自动订阅)
EventBus.on(EventType.PAGE_ENTER, (event) => {
console.log(`[状态机] 页面进入事件 → 当前状态: ${stateMachine.currentState}`);
stateMachine.dispatch('page_enter', event.payload);
});
// 发送事件(业务方调用)
EventBus.emit({
type: EventType.USER_ACTION,
payload: {
element_id: 'submit_btn',
action_type: 'click',
page_id: 'checkout',
timestamp: Date.now()
},
meta: {
session_id: getSessionId()
}
});
4.4 事件优先级与队列处理
为了防止高并发操作导致状态混乱,所有事件先进入 有序队列,按优先级和到达时间排序。超时事件和错误事件拥有最高优先级,确保状态机及时做出故障响应。
⚠️ 常见陷阱:不当使用同步派发(如直接在 UI 线程触发网络变化事件)会导致状态机上下文脏读。SDK 内部应使用
requestIdleCallback 或微任务队列异步处理事件。// 队列调度核心逻辑(伪代码)
class EventQueue {
dispatch(event) {
this.queue.enqueue(event, priorityMap[event.type]);
if (!this.processing) {
this.processing = true;
scheduleMicrotask(() => this.process());
}
}
process() {
while (this.queue.length > 0) {
const event = this.queue.dequeue();
const handler = this.router.route(event.type, stateMachine.currentState);
if (handler) {
handler.handle(event);
}
}
this.processing = false;
}
}
4.5 事件触发流程与状态机关联
4.6 调试与日志最佳实践
事件驱动状态机中,日志是定位问题的最直接手段。建议输出以下关键日志点:
- 事件入队前:打印事件类型、负载大小、时间戳
- 事件路由后:记录是否命中处理器、若未命中则输出警告
- 状态转换后:输出新旧状态、触发事件ID、耗时
- 异常事件:记录完整堆栈并打上高优先级标记
// 日志输出示例(结构化日志)
[INFO] [EventRouter] 事件入队 | type=page_enter id=evt-0001 size=1.2KB
[INFO] [EventRouter] 命中处理器 PageEnterHandler | 优先级 2
[INFO] [StateMachine] 状态跃迁 | event=page_enter from=FOREGROUND to=PAGE_FOCUSED
[WARN] [EventRouter] 未找到处理器 | type=timeout currentState=IDLE (忽略)
[ERROR] [StateMachine] 状态转换异常 | event=error currentState=FOCUSED stack=TypeError: ...
4.7 常见错误与性能优化
| 常见错误 | 原因 | 解决方案 |
|---|---|---|
| 事件重复触发 | UI 事件未去重或状态回调重复注册 | 在事件队列层增加重复检测(基于 event_id) |
| 状态机死锁 | 事件处理中修改了状态机的上下文导致递归 | 规定事件处理器内不允许直接调用 dispatch |
| 队列积压 | 高频事件(如滚动)未做 throttle | 引入事件节流:相同类型事件最小间隔 50ms |
| 异步事件顺序错乱 | 网络变化与页面进入事件交叉到达 | 使用事件版本号 + 时间戳排序,或使用同步队列 |
性能基线(参考):单事件从入队到状态转换完成应控制在 5ms 以内(含日志输出)。若超过 15ms,需检查事件处理器中的同步 IO 或长循环。