第2章 主线程与工作线程划分
单线程串行事件处理是AOSP无障碍服务默认的线程模型核心前提。AccessibilityService实例的所有生命周期回调、事件分发、onAccessibilityEvent()和onInterrupt()均委派给单一的主线程(通常称为main thread)。这一设计大幅降低了内部状态管理的复杂度,但也在长时间操作或阻塞调用场景下直接导致ANR风险。工作线程划分的目标正是将耗时计算、IO、模型推理等任务剥离出主线程,同时保持事件顺序和UI响应性。
2.1 边界定义
| 维度 | 主线程边界 | 工作线程边界 |
|---|---|---|
| 负责的生命周期 | onServiceConnected(), onAccessibilityEvent(), onInterrupt(), onUnbind() |
自定义任务(解析、过滤、推理、日志) |
| 数据所有权 | 短期事件对象(AccessibilityEvent引用) |
派生数据(结构化记录、缓存快照) |
| 可访问资源 | UI树、AccessibilityService本身 |
数据库、网络、本地模型文件 |
| 必须避免的操作 | 阻塞、锁竞争、内存分配循环 | 直接修改AccessibilityService状态 |
2.2 分层与组件职责
- 主线程层:运行系统创建的
ActivityThread主Looper,负责事件入队、分发、轻量预处理及最终UI反馈。通过Handler将工作线程结果回写到主线程。 - 工作线程层:开发者自行管理,推荐使用
ThreadPoolExecutor或HandlerThread。核心职责包括:结构化信息提取、NLP/OCR模型推理、本地数据持久化、远程上报。 - 通信层:以
Handler+MessageQueue为主线的异步消息传递机制。工作线程通过post/sendMessage将结果发送到主线程Handler,主线程通过obtainMessage发出任务指令。
🔑 关键原则:主线程只做事件分发与状态读取,所有耗时操作必须显式委托给工作线程;工作线程不持有
AccessibilityService直接引用,只通过消息传递结果。2.3 数据流与控制流
- 控制流方向:
SystemServer→AccessibilityService主线程→Handler.sendMessage()→MessageQueue→工作线程池→ 处理 →post Runnable→ 主线程Handler →onAccessibilityEvent回调(已更新状态)。 - 数据流载体:主线程通过
Message.obj或Bundle传递原始事件轻量拷贝;工作线程产出结构化结果(JSON、ProtoBuf或Entity),通过Handler.obtainMessage()回传。 - 信任链路:主线程只信任从工作线程投递的消息对象类型(强类型检查),工作线程不信任外部队列的未校验数据(输入净化是工作线程自身职责)。
2.4 选型权衡
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| HandlerThread | 天然序列化、避免竞态 | 单通道吞吐上限低 | 事件按序处理,任务量≤1000/秒 |
| ThreadPoolExecutor | 高并发、可调内核数 | 需要额外同步逻辑 | CPU密集型OCR/ML推理,任务可并行 |
| 协程(Kotlin) | 轻量、结构化并发 | 依赖协程上下文切换,调试复杂 | Android 12+ 新项目,团队熟悉协程 |
| WorkManager | 延迟可靠、保活性强 | 不适合毫秒级实时响应 | 日志上报、离线分析等非实时后台任务 |
⚠️ 风险点:
• 主线程饥饿:工作线程若频繁向主线程post大量高优先级任务,会导致事件分发延迟,引发ANR预兆(
• 死锁:主线程等待工作线程结果时,工作线程又等待主线程释放锁。
• 竞态条件:多个工作线程同时更新主线程Handler消息序列时,若未使用
• 线程泄漏:工作线程持有
• 主线程饥饿:工作线程若频繁向主线程post大量高优先级任务,会导致事件分发延迟,引发ANR预兆(
InputDispatcher超时)。• 死锁:主线程等待工作线程结果时,工作线程又等待主线程释放锁。
• 竞态条件:多个工作线程同时更新主线程Handler消息序列时,若未使用
synchronized或Handler.sendMessageAtFrontOfQueue滥用,造成事件乱序。• 线程泄漏:工作线程持有
Context或Handler隐式引用,导致AccessibilityService无法被GC回收。2.5 落地步骤
- 识别边界操作:审计
onAccessibilityEvent中的所有方法调用,耗时超过5ms的标记为“工作线程候选”。 - 定义线程模型:根据并发需求选择
HandlerThread或ThreadPoolExecutor,核心参数配置在onServiceConnected中初始化。 - 实现通信适配器:封装
MainHandler单例,提供postToWorker()和postResult()方法。 - 编写单元测试:使用
Robolectric模拟主线程Looper,验证工作线程任务是否在预期线程执行,消息投递是否有序。 - 上线前压力测试:模拟高频事件流(200次/秒),确保工作线程池未打满、主线程消息队列长度稳定在<100。
2.6 验收标准
- ✅
onAccessibilityEvent中无任何阻塞调用(sleep()、同步IO、Future.get())。 - ✅ 工作线程池线程数稳定,无内存泄漏(通过
LeakCanary验证Activity/Service泄漏)。 - ✅ 主线程ANR触发阈值(5秒)下,事件处理延迟不超过30ms。
- ✅ 所有工作线程结果回传使用
Handler.post(),不存在直接修改AccessibilityService实例的情况。 - ✅ 线程间通信覆盖
Message.what、Message.obj、Runnable三种模式,异常传递(RemoteException)有统一兜底。