【问题标题】:Timing of NotifyDataSetChanged() and calls to getView()NotifyDataSetChanged() 和调用 getView() 的时间
【发布时间】: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 线程。我将更新问题以反映这一点。

标签: android android-listview


【解决方案1】:

notifyDataSetChanged() 仅在其关联的观察者上触发 onChanged() 方法。

对于AdapterView 子类(例如ListView),这最终会调用requestLayout(),即schedules a layout pass of the view tree

所以,简而言之,它不是即时的。最终,将为每个项目调用getView(),但不会在notifyDataSetChanged() 完成之前调用。

无论如何,从工作线程调用notifyDataSetChanged() 不是一个好主意。事实上,从后台线程修改 Adapter 的内容很可能会产生异常。消息通常是:

适配器的内容发生了变化,但 ListView 没有收到 通知。确保我们适配器的内容没有被修改 来自后台线程,但仅来自 UI 线程。确保您的 适配器在其内容更改时调用 notifyDataSetChanged()。

【讨论】:

  • 我正在从 runOnUIThread() 中的可运行对象调用 notifyDataSetChanged。在屏幕上的跟踪中,所有适配器的 TID 都显示为主 UI 线程。我已更新问题以反映这一点。
  • @user316117。好吧,我没有得到那个。无论如何,答案是一样的:notifyDataSetChanged() 不会立即调用 getView()。
  • 好的,我将把这个特定问题算作已回答,我将开始一个关于其影响的新问题link
猜你喜欢
  • 2018-11-29
  • 1970-01-01
  • 2021-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多