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
相关产品推荐
相关产品推荐

