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 事件触发流程与状态机关联

事件类型与触发机制 — 状态机路由流程 事件源(六大类型 + 自定义) 应用启动 页面进入 用户操作 网络变化 超时 错误 业务自
定义 事件路由器 类型校验 / 优先级排队 状态机 当前状态 转换逻辑 埋点输出 调试日志记录 说明:事件源经过路由器统一分发到状态机,状态机根据当前状态和事件类型执行转换,最终输出埋点记录与调试日志。 事件统一载荷结构(JSON) { "event_id": "uuid-v4", "type": "page_enter | user_action | ...", "timestamp": 1716475200000, "payload": { "page_name": "home", "referrer": null }, "meta": { "session_id": "abc123", "priority": 2 } }

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 或长循环。