第3章 单次埋点状态生命周期:创建 · 激活 · 完成 · 清理
埋点事件在客户端从产生到上报、最终释放的整个过程中,遵循一个严格的状态机模型。每个埋点实例在内存中只经历四个有序状态:创建 → 激活 → 完成 → 清理。本章围绕该生命周期的边界、分层职责、信任链路展开,并给出落地步骤与验收标准。
3.1 边界定义
状态生命周期边界取决于以下三个因素:
- Compose 重组边界:埋点实例的创建通常发生在组合(Composition)阶段,清理发生在重组撤销或组件移出组合时。
- ViewModel 作用域:与屏幕/组件绑定的埋点应在 ViewModel 的
onCleared()或viewModelScope中进行清理。 - 数据层持久化边界:未完成的埋点数据需在状态到达完成后才允许写入本地缓存或上报队列。
边界原则:一个埋点实例一旦进入清理状态,其所有引用必须被显式置空,避免被后续重组意外恢复。
3.2 分层与组件职责
| 分层 | 组件 | 职责 |
|---|---|---|
| UI 层 | TrackStateHolder |
响应 Composable 生命周期,触发创建与清理;通过 remember / DisposableEffect 绑定。 |
| 业务层 | TrackStateMachine |
维护状态机实例,处理激活→完成转换,管理并发与重试逻辑。 |
| 数据层 | TrackRepository |
仅接受完成状态的埋点数据,执行序列化、加密、入队。 |
3.3 数据流 / 控制流 / 信任链路
- 控制流:
TrackStateHolder通过StateFlow<TrackState>将状态暴露给业务层,业务层监听状态变更并执行转换。 - 数据流:有效载荷(事件名、参数、时间戳)只在激活→完成阶段可被修改;完成之后冻结,不允许再变更。
- 信任链路:上层只能将状态推进(如从激活到完成),不可回退;唯一例外是异常发生时,业务层可强制将激活或完成中的实例直接跳转至清理,并丢弃数据。
信任规则:数据层只信赖
完成 状态携带的载荷。任何 创建 或 激活 状态的数据都不可被持久化,否则会导致脏数据。
3.4 选型权衡
| 决策项 | 方案 A | 方案 B | 推荐 |
|---|---|---|---|
| 状态表示 | sealed class + StateFlow |
enum + 可变引用 |
sealed class(类型安全,可携带不同载荷) |
| 生命周期绑定 | DisposableEffect + remember |
LaunchedEffect + 手动清理 |
DisposableEffect(保证清理时机与 Composition 一致) |
| 并发控制 | Mutex 同步状态机 |
AtomicReference 乐观锁 |
Mutex(简单可靠,避免竞态) |
3.5 风险点与对应策略
- 内存泄漏:埋点实例在清理后仍被 Composable 持有。
策略:在DisposableEffect的onDispose中显式调用release()。 - 重复创建:重组导致同一埋点被多次实例化。
策略:使用remember配合稳定的key(如eventId+screenId)。 - 状态丢失:进程被杀死前未达到完成状态。
策略:添加SavedStateHandle备份当前状态,重建时恢复。 - 竞态条件:多个异步操作同时修改状态。
策略:状态机内部使用Mutex保护所有转换方法。
3.6 落地步骤
- 定义 Sealed 状态
sealed class TrackState { object Creating : TrackState() data class Active(val payload: TrackPayload) : TrackState() data class Completed(val payload: TrackPayload) : TrackState() object Released : TrackState() } - 实现状态机类,提供
activate()、complete()、forceRelease()方法。 - 在 Composable 中绑定生命周期
val eventHolder = remember { TrackEventHolder(eventId) } DisposableEffect(eventHolder) { eventHolder.activate() onDispose { eventHolder.forceRelease() } } - 数据层订阅完成状态:通过
snapshotFlow或collect监听Completed状态并执行上报。 - 添加单元测试:覆盖正常流转、异常清理、重复绑定等场景。
3.7 验收标准
验收清单(可通过自动化测试验证)
- 埋点实例从创建到清理最多经历一次完整状态转换路径;不允许跳过完成直接释放(异常路径除外)。
- 任何处于创建或激活状态的实例被进程杀后恢复,能正确重试或丢弃(由策略决定)。
- 清理后的实例
payload引用为null,且StateFlow不再发出任何值。 - 并发 10 个线程同时调用
complete()时只有第一个生效,后续调用被阻塞或忽略。 - 使用
DisposableEffect的 Composable 在重组期间不会产生额外的创建操作(通过rememberkey 验证)。