Android 7系统无障碍服务(四)------AccessibilityEvent 的产生与投递
系列目录 :第一篇:全景图与架构概览 | [第二篇:AccessibilityManagerService 启动与初始化](#第二篇:AccessibilityManagerService 启动与初始化) | 第三篇:无障碍服务的注册与绑定 | [第四篇:AccessibilityEvent 的产生与投递](#第四篇:AccessibilityEvent 的产生与投递) | [第五篇:AccessibilityNodeInfo 的构建与查询](#第五篇:AccessibilityNodeInfo 的构建与查询) | [第六篇:输入事件拦截与 TouchExplorer](#第六篇:输入事件拦截与 TouchExplorer) | [第七篇:手势分发与 KeyEvent 处理](#第七篇:手势分发与 KeyEvent 处理) | 第八篇:放大镜与辅助功能特性
上一篇分析了无障碍服务从 Manifest 声明到被 AMS 绑定并就绪的全过程。服务就绪后,系统如何将 UI 变化告知它?答案就是 AccessibilityEvent。本篇完整梳理 AccessibilityEvent 从 View 层产生、经 AccessibilityManager 跨进程投递、在 AMS 中排队与调度、最终到达 AccessibilityService 的全链路。
一、AccessibilityEvent 是什么
AccessibilityEvent 是系统将 UI 变化通知无障碍服务的载体。它携带了"什么组件发生了什么变化"的上下文信息。
源码路径 :frameworks/base/core/java/android/view/accessibility/AccessibilityEvent.java
java
public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
// 核心事件类型(部分)
public static final int TYPE_VIEW_CLICKED = 0x00000001;
public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002;
public static final int TYPE_VIEW_SELECTED = 0x00000004;
public static final int TYPE_VIEW_FOCUSED = 0x00000008;
public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010;
public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020;
public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040;
public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
public static final int TYPE_VIEW_SCROLLED = 0x00001000;
public static final int TYPE_ANNOUNCEMENT = 0x00004000;
public static final int TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000;
public static final int TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000;
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
private int mEventType;
private CharSequence mPackageName;
private long mEventTime;
// 继承自 AccessibilityRecord: mSourceNodeId, mWindowId, mClassName, mText 等
}
二、事件在 View 层的产生
源码路径 :frameworks/base/core/java/android/view/View.java
java
public void sendAccessibilityEvent(int eventType) {
if (mAccessibilityDelegate != null) {
mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
} else {
sendAccessibilityEventInternal(eventType);
}
}
private void sendAccessibilityEventInternal(int eventType) {
if (AccessibilityManager.getInstance(mContext).isEnabled()) {
sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
}
}
public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
if (!isShown()) return; // 不可见 View 不发送
onInitializeAccessibilityEvent(event);
if (mParent != null) {
mParent.requestSendAccessibilityEvent(this, event);
}
}
两个过滤点:isEnabled() 全局开关检查 + isShown() 可见性检查。
典型触发场景:
- TYPE_VIEW_CLICKED →
View.performClick()中触发 - TYPE_VIEW_TEXT_CHANGED →
TextView中携带fromIndex/removedCount/addedCount/beforeText - TYPE_WINDOW_CONTENT_CHANGED → 通过
notifySubtreeAccessibilityStateChanged()沿 View 树向上传播至ViewRootImpl - TYPE_WINDOW_STATE_CHANGED →
ViewRootImpl在 Activity/Dialog/输入法窗口变化时主动发送
onInitializeAccessibilityEvent() 各控件可重写来填充特有信息(如 CompoundButton 填充 checked 状态)。
三、AccessibilityManager 客户端层
源码路径 :frameworks/base/core/java/android/view/accessibility/AccessibilityManager.java
java
public void sendAccessibilityEvent(AccessibilityEvent event) {
synchronized (mLock) {
if (!mIsEnabled) {
event.recycle();
return;
}
// 通过 Handler 异步发送,避免阻塞 UI 线程
mHandler.sendMessage(mHandler.obtainMessage(
DO_SET_ACCESSIBILITY_EVENT, event));
}
}
关键设计:异步发送 。sendAccessibilityEvent() 通常在 UI 线程被调用,如果同步等待 Binder 调用完成会造成 UI 卡顿。通过 Han