AccessibilityDelegate 是 Android 无障碍服务的一部分,每个 View 都可以设置一个 AccessibilityDelegate 来拦截无障碍事件(包括点击)。当 TalkBack 等无障碍服务激活时,用户的触摸交互会通过 sendAccessibilityEvent 回调。利用这一机制可以实现”零代码侵入”的全埋点。
一、核心原理
当辅助功能服务启用时,系统会将触摸事件转换为 AccessibilityEvent 发送给 View。通过在 View 树根节点设置自定义 AccessibilityDelegate,可以在 sendAccessibilityEvent 回调中捕获点击事件(TYPE_VIEW_CLICKED)。
class TrackingAccessibilityDelegate( |
二、全局设置
class TrackingApplication : Application() { |
三、优劣势分析
优势:
- 真正的零业务代码侵入,无需修改
setOnClickListener - 无需反射私有字段
mOnClickListener,比 OnClickListener 代理更合规 - 能捕获通过无障碍触发的点击(如 TalkBack 双击),覆盖盲操场景
局限性:
- 严重依赖辅助功能状态:若用户未开启 TalkBack 等无障碍服务,
sendAccessibilityEvent不会触发点击事件,埋点数据将大量丢失 - 兼容性风险:部分 ROM 厂商对 AccessibilityDelegate 有定制行为
- 性能额外开销:每次点击事件需要额外生成
AccessibilityEvent对象
四、适用场景
由于严格依赖无障碍服务,该方案不作为独立的全埋点方案使用,通常作为以下场景的补充:
- 无障碍友好型 App 的埋点补充
- 与其他方案(如 Window.Callback)组合,覆盖特殊场景
面试常考问题
Q1:AccessibilityDelegate 全埋点为什么不能作为主方案?
因为 sendAccessibilityEvent 中的 TYPE_VIEW_CLICKED 事件只有在系统无障碍服务(如 TalkBack、Switch Access)激活时才会有系统将其触摸转换为 AccessibilityEvent。在普通用户场景(未开启辅助功能),点击操作不会经过 AccessibilityDelegate,因此无法捕获点击。AOSP 源码路径:View.java 中的 onInitializeAccessibilityEvent() 和 sendAccessibilityEvent() 方法。
Q2:能否通过代码强制开启无障碍服务来弥补?
不能。无障碍服务需要在系统设置中由用户手动开启,Android 出于安全和隐私考虑不允许应用通过代码自启动无障碍服务。部分厂商提供了快捷跳转到无障碍设置页面的 Intent,但最终开关权在用户手中。
Q3:如何区分真实点击和 TalkBack 模拟点击?
在 sendAccessibilityEvent 中可以通过 host.isScreenReaderFocusable() 或检查 AccessibilityEvent 的来源(getSource())来辅助判断,但无法可靠区分触摸点击和无障碍模拟点击。如果需要精确区分,建议配合 Window.Callback 代理方案使用。


