Android 7系统无障碍服务(四)AccessibilityEvent 的产生与投递

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_CLICKEDView.performClick() 中触发
  • TYPE_VIEW_TEXT_CHANGEDTextView 中携带 fromIndex/removedCount/addedCount/beforeText
  • TYPE_WINDOW_CONTENT_CHANGED → 通过 notifySubtreeAccessibilityStateChanged() 沿 View 树向上传播至 ViewRootImpl
  • TYPE_WINDOW_STATE_CHANGEDViewRootImpl 在 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

相关推荐
又见情义12 分钟前
RK3568 Android 13 屏蔽 healthd 电池日志经验分享
android
古法安卓3 小时前
Android-Fork 机制详解
android·java·android studio
Android打工仔4 小时前
不要在 Data 层随意把 Cold Flow 转换成 Hot Flow
android·架构·kotlin
峥嵘life4 小时前
2026免费的 opencode 使用分享:Windows端 + 服务器CLI 实战总结
android·大数据·开发语言
杉氧5 小时前
拒绝重复造轮子:我写了一个生产级的 Kotlin 协程与 Flow 工具库(CoroutineKit)
android·kotlin·workflow
恋猫de小郭5 小时前
Flutter GSoC 2026 提案进度解读,补上 DevTools、FFI 和原生平台的关键缺口
android·前端·flutter
2501_915909065 小时前
怎么用 FlutterFlow 把应用发布到 App Store?
android·ios·小程序·https·uni-app·iphone·webview
峥嵘life5 小时前
2026华为AI码道 CodeArts 使用分享:Windows端 + 服务器CLI 实战总结
android·大数据·开发语言·python
yxlalm6 小时前
对话记忆持久化-Redis热与MySQL冷
android·redis·mysql