You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Accessibility Service调用event.getSource()引发ANR及异常的解决咨询

问题原因分析

你遇到的两个问题本质是Accessibility服务的线程约束导致的:

  • 主线程调用event.getSource()并操作节点时,若节点层级复杂、遍历耗时过长,会阻塞服务主线程触发ANR;
  • 直接将AccessibilityNodeInfo或AccessibilityEvent对象传到子线程,这些对象在onEvent方法返回后会被系统标记为密封(sealed)状态,子线程无法再操作,从而抛出IllegalStateException。
解决方案

核心原则:所有涉及AccessibilityNodeInfo和AccessibilityEvent的操作必须在onEvent的主线程中完成,仅将提取后的纯数据(字符串、基本类型、自定义数据结构)传到子线程处理。

步骤1:重构数据模型

修改AccessibilityEventModel,移除对AccessibilityNodeInfo和Parcelable对象的引用,只保留需要的纯数据字段:

public class AccessibilityEventModel {
    private String packageName;
    private String eventText;
    private String eventClassName;
    // 新增你需要的节点信息(示例)
    private String nodeText;
    private String viewIdResourceName;

    // 省略getter、setter方法
}

步骤2:主线程处理节点,提取数据

在OnEvent方法中,主线程内完成节点信息提取、资源回收,再将纯数据提交到子线程:

private void OnEvent(final Context context, AccessibilityEvent event) {
    String eventText = "", eventPackageName = "", eventClassName = "";
    String nodeText = "", viewId = "";
    AccessibilityNodeInfo eventSource = null;

    try {
        // 提取事件基础信息
        if (event.getText() != null && event.getText().size() > 0)
            eventText = event.getText().toString();
        if (event.getPackageName() != null)
            eventPackageName = event.getPackageName().toString();
        if (event.getClassName() != null)
            eventClassName = event.getClassName().toString();

        // 主线程内处理AccessibilityNodeInfo
        eventSource = event.getSource();
        if (eventSource != null) {
            // 提取需要的节点数据
            nodeText = eventSource.getText() != null ? eventSource.getText().toString() : "";
            viewId = eventSource.getViewIdResourceName() != null ? eventSource.getViewIdResourceName() : "";
            // 若需遍历子节点,也在此处完成,将结果存入Model(如List<String>子节点文本)
            
            // 用完立即回收节点,避免内存泄漏
            eventSource.recycle();
        }

        // 封装纯数据Model
        final AccessibilityEventModel accessibilityEventModel = new AccessibilityEventModel();
        accessibilityEventModel.setPackageName(eventPackageName);
        accessibilityEventModel.setEventText(eventText);
        accessibilityEventModel.setEventClassName(eventClassName);
        accessibilityEventModel.setNodeText(nodeText);
        accessibilityEventModel.setViewIdResourceName(viewId);

        // 子线程处理纯数据
        executorService.execute(() -> {
            AccessibilityManager accessibilityManager = new AccessibilityManager(context, localDatabaseSource);
            accessibilityManager.goThroughAccessibilityEvent(event.getEventType(), accessibilityEventModel);
        });

    } catch (Exception e) {
        Log.d(TAG, " Error: " + e.getMessage());
    } finally {
        // 双重确保节点资源被回收
        if (eventSource != null && !eventSource.isRecycled()) {
            eventSource.recycle();
        }
    }
}

步骤3:子线程处理纯数据

修改Kotlin方法,确保仅操作纯数据,不再接触Accessibility相关对象:

fun goThroughAccessibilityEvent(
    eventType: AccessibilityEventType,
    accessibilityEventModel: AccessibilityEventModel
) {
    when (eventType) {
        AccessibilityEventType.TYPE_WINDOW_CONTENT_CHANGED -> WindowContentChangeEventData(
            localDatabaseSource
        ).onAccessibilityEvent(context, accessibilityEventModel)
        // 此处onAccessibilityEvent仅处理Model中的纯数据即可
    }
}
关键注意事项
  • 绝对禁止在子线程中调用任何AccessibilityNodeInfo的方法(如getText()、getChild()),所有节点操作必须在主线程完成;
  • 每次获取AccessibilityNodeInfo后,务必调用recycle()释放资源,防止内存泄漏;
  • 若需复杂节点遍历,尽量优化逻辑(如只遍历目标节点,避免全量遍历),减少主线程耗时。

内容的提问来源于stack exchange,提问作者Ahmad Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 23:52:37