第4章 事件定义与触发:崩溃发生时、网络变化、定时器、手动触发、优先级处理

状态机由事件驱动。事件是状态转换的唯一原因,没有事件就没有流转。本章将明确定义4种核心事件,设计可扩展的事件数据模型,建立优先级仲裁机制,并完成事件源到状态机的完整链路接入。

4.1 事件体系总览

崩溃上报状态机需要处理不同来源、不同紧急程度的外部刺激。下表给出了4种事件类型的核心特征对比:

事件类型 来源 触发方式 默认优先级 说明
崩溃发生 (CrashEvent) Native/Java Crash 信号处理器 / 异常捕获 最高 核心事件,触发整个上报链路启动
网络变化 (NetworkEvent) 系统广播 / NetworkCallback 被动接收 高 影响上报策略、重试时机和日志聚合
定时器 (TimerEvent) AlarmManager / Handler 时间到达 中 用于延迟聚合、强制上报、保活心跳
手动触发 (ManualEvent) 外部开发API调用 外界请求 低 用户反馈反馈、开发者日志同步、设置变更

4.2 事件定义与数据结构

为了类型安全和可扩展性,我们使用Kotlin密封类(Sealed Class)定义所有事件类型。每个事件携带自身特有的元数据,并显式声明优先级权重:

核心代码:事件密封类定义

sealed class UploadEvent(val priority: Int) {

    data class CrashEvent(
        val timestamp: Long,
        val threadName: String,
        val stackTrace: String,
        val priority: Int = Int.MAX_VALUE
    ) : UploadEvent(priority)

    data class NetworkEvent(
        val isAvailable: Boolean,
        val type: Int, // WIFI, MOBILE, NONE
        val priority: Int = 100
    ) : UploadEvent(priority)

    data class TimerEvent(
        val taskId: String,
        val triggerTime: Long,
        val priority: Int = 50
    ) : UploadEvent(priority)

    data class ManualEvent(
        val reason: String,
        val priority: Int = 10
    ) : UploadEvent(priority)
}

设计要点: Int.MAX_VALUE 确保崩溃事件在优先级队列中永远排在队首。网络事件使用 100 作为高优先级基数,以便在未来扩展子优先级(如WIFI为101,移动网络为99)。

4.3 事件触发原理解析

4.3.1 崩溃发生时(CrashEvent)

Thread.setDefaultUncaughtExceptionHandler 捕获Java层未处理异常;JNI层通过 sigaction() 注册 SIGABRT、SIGSEGV 等信号处理器捕获Native崩溃。在信号处理器上下文中,只做最少工作——将原始 siginfo_t 和 ucontext 写入共享内存(shmem),立即返回。事件循环从shmem中读取数据,组装 CrashEvent 并注入状态机。

信号处理器安全规则: 信号处理器内部禁止调用 malloc、free、pthread_mutex_lock 等不可重入函数。一旦在信号处理器中再次崩溃,必须调用 _exit() 终止进程,防止无限循环