将AccessibilityEvent传入后台线程后属性值丢失问题咨询
解决AccessibilityEvent传入AsyncTask后属性丢失的问题
这个问题我之前也碰到过,其实是Android系统对AccessibilityEvent的复用优化机制搞的鬼!
原因分析
Android为了提升性能,不会每次都创建新的AccessibilityEvent对象,而是维护了一个对象池。当onAccessibilityEvent()方法执行完毕后,系统会把当前的事件对象回收进池子里,同时清空它的所有属性(比如mEventTime置0、mPackageName设为null),等待下一次事件触发时复用。而你的AsyncTask是在后台线程执行,大概率会比onAccessibilityEvent()方法晚完成,这时候原来的事件对象已经被系统重置了,自然拿不到有效属性。
两种解决方案
1. 复制AccessibilityEvent对象
通过AccessibilityEvent.obtain(event)方法复制一个独立的事件副本,这个副本不会被系统回收,AsyncTask可以安全地使用它的所有属性。注意用完之后一定要调用recycle()释放资源,避免内存泄漏。
修改你的代码如下:
public class MyAccessibilityService extends AccessibilityService { public void onAccessibilityEvent(AccessibilityEvent event) { // 复制事件对象,生成独立副本 AccessibilityEvent copiedEvent = AccessibilityEvent.obtain(event); new MyTestAsync().execute(copiedEvent); } public class MyTestAsync extends AsyncTask<Object, Void, Void> { private AccessibilityEvent event; @Override protected Void doInBackground(Object... params) { this.event = (AccessibilityEvent) params[0]; // 这里可以正常访问event的所有属性了 // 用完后记得回收副本 event.recycle(); return null; } } }
2. 提前提取所需属性
如果你不需要完整的AccessibilityEvent对象,更高效的方式是在onAccessibilityEvent()里先把需要的属性提取出来,封装成自定义实体类再传给AsyncTask。这种方式避免了对象复制的开销,也不用处理回收逻辑。
示例代码:
// 自定义实体类,存储需要的事件属性 class AccessibilityEventData { long eventTime; String packageName; // 可以添加其他你需要的属性,比如事件类型、文本内容等 } public class MyAccessibilityService extends AccessibilityService { public void onAccessibilityEvent(AccessibilityEvent event) { AccessibilityEventData eventData = new AccessibilityEventData(); // 提前提取属性 eventData.eventTime = event.getEventTime(); if (event.getPackageName() != null) { eventData.packageName = event.getPackageName().toString(); } // 提取其他需要的属性... new MyTestAsync().execute(eventData); } public class MyTestAsync extends AsyncTask<AccessibilityEventData, Void, Void> { @Override protected Void doInBackground(AccessibilityEventData... params) { AccessibilityEventData data = params[0]; // 直接使用data里的属性,完全不会有丢失问题 return null; } } }
内容的提问来源于stack exchange,提问作者Nikhil




