【发布时间】:2014-05-26 18:26:31
【问题描述】:
我正在调试一个带有 ListView 和 2 个线程的 Android 应用程序 - 一个更新数据存储的工作线程和一个执行所有常见 View 内容(包括显示列表视图)的主 UI 线程。 (这应该是一个快速调试,所以我没有义务改变这个架构)。我发现存在线程冲突,因为当 getView() 尝试使用数据存储区中的内容时,工作线程有时正在运行。
因此,为了防止这些冲突,我创建了一个计数为 1 的信号量,并且工作线程必须在接触数据存储阵列之前获取它。我还覆盖了 notifyDataSetChanged() 并在其中放置了一个 aquire 和 release 以及一些日志记录来检测它。
@Override
public void notifyDataSetChanged() {
try {
MyActivity.datastoreSemaphore.acquire();
Log.d ("notifyDataSetChanged()...", " acquired semaphore " );
try {
super.notifyDataSetChanged();
Log.d ("notifyDataSetChanged()...", "listItems.size()=" + String.valueOf(listItems.size()) + " lv.getCount()=" + String.valueOf(lv.getCount()) ); //!! debugging
} catch (Exception e) {
Log.e ("notifyDataSetChanged() inner catch ", "Exception = " + e ); debugging
} finally {
MyActivity.datastoreSemaphore.release();
Log.d ("notifyDataSetChanged()...", " released semaphore " );
}
} catch (InterruptedException e) {
Log.e ("notifyDataSetChanged() outer catch ", "Exception = " + e );
}
}
我还检测了 getView() 和 这是我没有得到的: 跟踪显示 getView() 在何时被调用 OUTSIDE notifyDataSetChanged() 被调用...... .
05-25 13:36:37.865: D/notifyDataSetChanged()...(21863): 获得 信号量 05-25 13:36:37.865: D/notifyDataSetChanged()...(21863): listItems.size()=14 lv.getCount()=14
05-25 13:36:37.865: D/notifyDataSetChanged()...(21863): 发布 信号量
05-25 13:36:37.869: D/进入 getView() ...(21863): position=0 Adapter的getCount()=14
05-25 13:36:37.873: D/in getView() ...(21863): listItems size =14 位置 = 0
05-25 13:36:37.873: D/Exiting getView() ...(21863): position=0 Adapter的getCount()=14
05-25 13:36:37.873: D/进入 getView() ...(21863): position=1 Adapter的getCount()=14
05-25 13:36:37.873: D/in getView() ...(21863): listItems size =14 位置 = 1
也就是说,根据LogCat,我们调用notifyDataSetChanged();我们获取信号量,调用基本方法,从基本方法返回,释放信号量,THEN getView() 循环开始。 . .
我的印象是 notifyDataSetChanged() 直到整个 getView() 循环完成后才会返回,但这似乎另有说明。这里发生了什么事? (注意,这个问题实际上与信号量或线程冲突无关 - 我提到那些是为了提供上下文 - 问题是关于 getView() 循环 WRT 的时间notifyDataSetChanged() .)
编辑:对 notifyDataSetChanged() 的调用通过 runOnUIThread() 中的可运行对象在主 UI 线程上运行。在屏幕上的跟踪中,所有适配器的 TID 都显示为主 UI 线程。我将更新问题以反映这一点。
【问题讨论】:
-
在我看来,你最好完全避免这个问题,确保在主线程上调用
notifyDataSetChanged()。您可以在MainLooper(info) 上使用Handler从工作线程跳转到主线程。 -
它是在主线程上调用的——当我调用它时,我会在 runOnUIThread() 中使用一个可运行的对象来执行它。在屏幕上的跟踪中,所有适配器的 TID 都显示为主 UI 线程。我将更新问题以反映这一点。