【问题标题】:Android, ListView IllegalStateException: "The content of the adapter has changed but ListView did not receive a notification"Android,ListView IllegalStateException:“适配器的内容已更改但ListView没有收到通知”
【发布时间】:2011-03-09 02:28:35
【问题描述】:

我想做的事:运行一个后台线程,计算 ListView 内容并部分更新 ListView,同时计算结果。

我知道我必须避免的事情:我不能弄乱后台线程中的 ListAdapter 内容,所以我继承了 AsyncTask 并从 onProgressUpdate 发布结果(向适配器添加条目)。我的适配器使用结果对象的 ArrayList,对这些 arraylist 的所有操作都是同步的。

其他人的研究:有非常有价值的数据here。对于大约 500 名用户,我几乎每天都会遇到崩溃,当我在 onProgressUpdate 中添加 list.setVisibility(GONE)/trackList.setVisibility(VISIBLE) 块时,崩溃降低了 10 倍,但并没有消失。 (建议在answer

我有时会得到什么:请注意,这种情况很少发生(每周一次,对于 3.5k 用户之一)。但我想完全摆脱这个错误。这是部分堆栈跟踪:

`java.lang.IllegalStateException:` The content of the adapter has changed but ListView  did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131296334, class android.widget.ListView) with Adapter(class com.transportoid.Tracks.TrackListAdapter)]
at android.widget.ListView.layoutChildren(ListView.java:1432)
at android.widget.AbsListView.onTouchEvent(AbsListView.java:2062)
at android.widget.ListView.onTouchEvent(ListView.java:3234)
at android.view.View.dispatchTouchEvent(View.java:3709)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:852)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
[...]

帮助?不再需要,见下文

最终答案:事实证明,我每插入 5 次就调用一次notifyDataSetChanged,以避免闪烁和突然的列表更改。不能这样做,当基本列表发生变化时总是通知适配器。这个错误现在对我来说早已消失了。

【问题讨论】:

  • 你调用 notifyDataSetChanged() 了吗?
  • 当然,在 onProgressUpdate 中有顺序:list.setVisibility(GONE) - addObjectToList/listed 上的同步操作/ - notifyDataSetChanged() - list.setVisibility(VISIBLE) (并且没有可见性修改异常发生很远更频繁)
  • 你是在不同的线程上修改底层的 ArrayList 吗?所有更改,甚至是适配器引用的 ArrayList,都必须在 UI 线程上发生。
  • @Qberticus - 正如我明确指出的,我不会从不同的线程修改 ArrayList,而是从 AcyncTask 的 onProgressUpdate 方法修改 - 它在 GUI 线程中工作。
  • @tomash 我遇到了同样的异常,我正在使用 pull 刷新并在执行后我写了 adapter.notifyDataSetChanged(); lvAutherlist.completeRefreshing(); 但有时会出现这个错误如何解决

标签: android exception listview adapter


【解决方案1】:

我编写了这段代码,并让它在 2.1 模拟器映像中运行了大约 12 个小时,并且没有收到 IllegalStateException。我要让 android 框架受益于对这个问题的怀疑,并说它很可能是你的代码中的一个错误。我希望这有帮助。也许您可以根据您的列表和数据对其进行调整。

public class ListViewStressTest extends ListActivity {
    ArrayAdapter<String> adapter;
    ListView list;
    AsyncTask<Void, String, Void> task;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        this.adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
        this.list = this.getListView();

        this.list.setAdapter(this.adapter);

        this.task = new AsyncTask<Void, String, Void>() {
            Random r = new Random();
            int[] delete;
            volatile boolean scroll = false;

            @Override
            protected void onProgressUpdate(String... values) {
                if(scroll) {
                    scroll = false;
                    doScroll();
                    return;
                }

                if(values == null) {
                    doDelete();
                    return;
                }

                doUpdate(values);

                if(ListViewStressTest.this.adapter.getCount() > 5000) {
                    ListViewStressTest.this.adapter.clear();
                }
            }

            private void doScroll() {
                if(ListViewStressTest.this.adapter.getCount() == 0) {
                    return;
                }

                int n = r.nextInt(ListViewStressTest.this.adapter.getCount());
                ListViewStressTest.this.list.setSelection(n);
            }

            private void doDelete() {
                int[] d;
                synchronized(this) {
                    d = this.delete;
                }
                if(d == null) {
                    return;
                }
                for(int i = 0 ; i < d.length ; i++) {
                    int index = d[i];
                    if(index >= 0 && index < ListViewStressTest.this.adapter.getCount()) {
                        ListViewStressTest.this.adapter.remove(ListViewStressTest.this.adapter.getItem(index));
                    }
                }
            }

            private void doUpdate(String... values) {
                for(int i = 0 ; i < values.length ; i++) {
                    ListViewStressTest.this.adapter.add(values[i]);
                }
            }

            private void updateList() {
                int number = r.nextInt(30) + 1;
                String[] strings = new String[number];

                for(int i = 0 ; i < number ; i++) {
                    strings[i] = Long.toString(r.nextLong());
                }

                this.publishProgress(strings);
            }

            private void deleteFromList() {
                int number = r.nextInt(20) + 1;
                int[] toDelete = new int[number];

                for(int i = 0 ; i < number ; i++) {
                    int num = ListViewStressTest.this.adapter.getCount();
                    if(num < 2) {
                        break;
                    }
                    toDelete[i] = r.nextInt(num);
                }

                synchronized(this) {
                    this.delete = toDelete;
                }

                this.publishProgress(null);
            }

            private void scrollSomewhere() {
                this.scroll = true;
                this.publishProgress(null);
            }

            @Override
            protected Void doInBackground(Void... params) {
                while(true) {
                    int what = r.nextInt(3);

                    switch(what) {
                        case 0:
                            updateList();
                            break;
                        case 1:
                            deleteFromList();
                            break;
                        case 2:
                            scrollSomewhere();
                            break;
                    }

                    try {
                        Thread.sleep(0);
                    } catch(InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

        };

        this.task.execute(null);
    }
}

【讨论】:

  • 感谢您的工作!我注意到对于 ArrayAdapter 方法 hasStableIds()=false;我的实现返回了 true,这不太正确,因为在每个 notifyDataSetChanged() 之前对行进行了排序。我会试一试 - 如果崩溃仍然存在,我将继续调查。最好的问候!
【解决方案2】:

即使我在 XMPP 通知应用程序中遇到了同样的问题,也需要将接收者消息添加回列表视图(使用 ArrayList 实现)。当我尝试通过MessageListener(单独的线程)添加接收器内容时,应用程序退出并出现上述错误。我通过 runOnUiThread 方法将内容添加到我的 arraylistsetListviewadapater 中解决了这个问题,该方法是 Activity 类的一部分。这解决了我的问题。

【讨论】:

    【解决方案3】:

    我遇到了同样的问题,我解决了。我的问题是我使用的是listview,带有数组适配器和过滤器。在 performFiltering 方法上,我弄乱了包含数据的数组,这是问题所在,因为此方法未在 UI 线程上运行,并且最终会引发一些问题。

    【讨论】:

      【解决方案4】:

      我在完全相同的错误日志中遇到了同样的问题。 在我的情况下,AsyncTask 的onProgress() 使用mAdapter.add(newEntry) 将值添加到适配器。为了避免 UI 变得反应迟钝,我设置了 mAdapter.setNotifyOnChange(false) 并每秒调用 mAdapter.notifyDataSetChanged() 4 次。每秒对数组进行一次排序。

      这很好用,看起来很容易上瘾,但不幸的是,经常触摸显示的列表项可能会使它崩溃。

      但我似乎找到了一个可以接受的解决方法。 我的猜测是,即使您只是在 ui 线程上工作,适配器也不会在不调用 notifyDataSetChanged() 的情况下接受对其数据的许多更改,因此我创建了一个队列来存储所有新项目,直到上述 300 毫秒结束。如果到了这一刻,我会一次性添加所有存储的项目并致电notifyDataSetChanged()。 到现在为止,我不能再让列表崩溃了

      【讨论】:

        【解决方案5】:

        我遇到了同样的问题。

        我在 UI 线程之外向我的 ArrayList 添加项目。

        解决方案:adding the items 和 UI 线程中的 notifyDataSetChanged() 我都做了。

        【讨论】:

        • 我都添加了项目并在 UI 线程中调用 notifyDataSetChanged() 并解决了这个问题。
        • 真诚的评论,真的。
        • 这是一个多线程问题,使用正确同步的块这是可以避免的。无需在 UI 线程上添加额外内容并导致应用程序失去响应能力。请在下面查看我的答案。
        • 对于未来的读者 - 在后台线程中进行计算是一件好事,但您应该将结果传递给 UI 线程并在同一代码块中将项目添加到适配器基础集合并通知适配器。
        • @Mullins 能否为您的解决方案展示一个示例?我遇到了同样的问题
        【解决方案6】:

        我遇到了同样的问题,但我用方法解决了它

        requestLayout();
        

        来自班级ListView

        【讨论】:

        • 什么时候调用这个方法?
        • @DeBuGGeR 在您将项目添加到 ListView 或更改它的适配器之后。
        • 如果我在 asynctask 中添加元素会怎样
        • 只是为了记录,@Dr.aNdRO,您在 AsyncTask 中收集您的结果。 doInBackground() 并保存结果以更新 AsyncTask.onPostExecute() 中的列表,该列表将在 UI 线程中运行。如果您需要随时更新,请使用也在 UI 线程中运行的 AsyncTask.publishProgress() 和 AsyncTask.onProgressUpdate()。
        【解决方案7】:

        导致此崩溃的一个原因是ArrayList 对象无法完全更改。 所以,当我删除一个项目时,我必须这样做:

        mList.clear();
        mList.addAll(newDataList);
        

        这为我解决了崩溃问题。

        【讨论】:

          【解决方案8】:

          在我的例子中,我从主 Activity 上的 TextWatcher() 方法调用适配器上的方法 GetFilter(),并在 GetFilter() 上添加了带有 For 循环的数据。 解决方案是将主 Activity 上的 For 循环更改为 AfterTextChanged() 子方法并删除对 GetFilter() 的调用

          【讨论】:

          • 请说明您的解决方案。我的情况也像你/
          【解决方案9】:

          请尝试以下解决方案之一:

          1. 有时,如果您在线程中(或doInBackground 方法)将新对象添加到数据列表中,则会出现此错误。解决方案是:创建一个临时列表并在线程中向该列表添加数据(或doInBackground),然后将临时列表中的所有数据复制到UI线程中的适配器列表(或onPostExcute

          2. 确保在 UI 线程中调用所有 UI 更新。

          【讨论】:

            【解决方案10】:

            我通过有 2 个列表解决了这个问题。我仅用于适配器的一个列表,我在另一个列表上进行所有数据更改/更新。这允许我在后台线程中对一个列表进行更新,然后在主/UI 线程中更新“适配器”列表:

            List<> data = new ArrayList<>();
            List<> adapterData = new ArrayList();
            
            ...
            adapter = new Adapter(adapterData);
            listView.setAdapter(adapter);
            
            // Whenever data needs to be updated, it can be done in a separate thread
            void updateDataAsync()
            {
                new Thread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        // Make updates the "data" list.
                        ...
            
                        // Update your adapter.
                        refreshList();
                    }
                }).start();
            }
            
            void refreshList()
            {
                runOnUiThread(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        adapterData.clear();
                        adapterData.addAll(data);
                        adapter.notifyDataSetChanged();
                        listView.invalidateViews();
                    }
                });
            }
            

            【讨论】:

              【解决方案11】:

              这是一个多线程问题,正确使用同步块这是可以预防的。 无需在 UI Thread 上添加额外的东西并导致应用程序失去响应能力。

              我也面临同样的问题。正如最被接受的答案所暗示的那样,从 UI Thread 更改适配器数据可以解决这个问题。这将起作用,但它是一种快速简便的解决方案,但不是最好的解决方案。

              正如您在正常情况下看到的那样。从后台线程更新数据适配器并在 UI 线程中调用 notifyDataSetChanged 有效。

              当 ui 线程正在更新视图并且另一个后台线程再次更改数据时,会出现此非法状态异常。那一刻导致了这个问题。

              因此,如果您要同步所有更改适配器数据并进行 notifydatasetchange 调用的代码。这个问题应该没了。对我来说已经过去了,我仍在从后台线程更新数据。

              这是我的案例特定代码,供其他人参考。

              我在主屏幕上的加载器在后台将电话簿联系人加载到我的数据源中。

                  @Override
                  public Void loadInBackground() {
                      Log.v(TAG, "Init loadings contacts");
                      synchronized (SingleTonProvider.getInstance()) {
                          PhoneBookManager.preparePhoneBookContacts(getContext());
                      }
                  }
              

              此 PhoneBookManager.getPhoneBookContacts 从电话簿中读取联系人并将其填充到哈希图中。可直接用于List Adapters绘制列表。

              我的屏幕上有一个按钮。这将打开一个列出这些电话号码的活动。 如果我在前一个线程完成其工作之前直接在列表上设置适配器,那么快速导航情况发生的频率会降低。它弹出异常。这是这个 SO 问题的标题。所以我必须在第二个活动中做这样的事情。

              我在第二个活动中的加载器等待第一个线程完成。直到它显示一个进度条。检查两个加载器的 loadInBackground。

              然后它创建适配器并将其传递给我在 ui 线程上调用 setAdapter 的活动。

              这解决了我的问题。

              此代码只是一个 sn-p。您需要对其进行更改以适合您的编译。

              @Override
              public Loader<PhoneBookContactAdapter> onCreateLoader(int arg0, Bundle arg1) {
                  return new PhoneBookContactLoader(this);
              }
              
              @Override
              public void onLoadFinished(Loader<PhoneBookContactAdapter> arg0, PhoneBookContactAdapter arg1) {
                  contactList.setAdapter(adapter = arg1);
              }
              
              /*
               * AsyncLoader to load phonebook and notify the list once done.
               */
              private static class PhoneBookContactLoader extends AsyncTaskLoader<PhoneBookContactAdapter> {
              
                  private PhoneBookContactAdapter adapter;
              
                  public PhoneBookContactLoader(Context context) {
                      super(context);
                  }
              
                  @Override
                  public PhoneBookContactAdapter loadInBackground() {
                      synchronized (SingleTonProvider.getInstance()) {
                          return adapter = new PhoneBookContactAdapter(getContext());    
                      }
                  }
              
              }
              

              希望对你有帮助

              【讨论】:

              • 抱歉,你是怎么做到的?你是如何同步后台线程代码和 notifydatasetchange 调用的?我在过滤 AutocompleteTextView 上的数据时使用了 doInBackground,我遇到了同样的问题......你能给我一些建议来解决吗?
              • @user3019105 - 同步两个后台线程。一种在后台更新数据,另一种是使用可用的 handler.post 方法或 runOnUiThread 方法通知 ui 线程 setAdadpter 或 notifydatasetchanged。对于我的特殊情况,我将两个加载器与它们的 doInBackground 同步在一个单例对象上。一开始准备闪屏本身的数据,以便快速用于仪表板屏幕。那是我的需要。
              • 能否请您发布此实现的代码的 sn-p ?就我而言,我已经解决了在适配器上调用 clear(),创建过滤对象的临时 ArrayList,然后调用 addAll(tmpArrayList),最后调用 notifyDataSetChanged()。我在 publishResult() Filter 方法中进行所有这些调用,在主线程上运行。您认为这是一个可靠的解决方案吗?
              • @user3019105 检查编辑后的答案。希望它现在对你有帮助
              • 这个答案需要大量阅读它的工作原理,tutorials.jenkov.com/java-concurrency/synchronized.html 是一个很好的起点
              【解决方案12】:

              如果这种情况间歇性发生,结果我只有在单击“加载更多”最后一项后滚动列表时才遇到此问题。如果列表没有滚动,一切正常。

              经过多次调试,这是我的一个错误,但 Android 代码也不一致。

              当验证发生时,这段代码在 ListView 中执行

                      } else if (mItemCount != mAdapter.getCount()) {
                          throw new IllegalStateException("The content of the adapter has changed but "
                                  + "ListView did not receive a notification. Make sure the content of "
              

              但是当 onChange 发生时,它会在 AdapterView(ListView 的父级)中触发此代码

                  @Override
                  public void onChanged() {
                      mDataChanged = true;
                      mOldItemCount = mItemCount;
                      mItemCount = getAdapter().getCount();
              

              注意适配器的方式不保证是相同的!

              在我的例子中,因为它是一个“LoadMoreAdapter”,所以我在 getAdapter 调用中返回 WrappedAdapter(用于访问底层对象)。由于额外的“加载更多”项目和抛出的异常,这导致计数不同。

              我这样做只是因为文档使它看起来可以这样做

              ListView.getAdapter javadoc

              返回当前在此 ListView 中使用的适配器。返回的 适配器可能与传递给的适配器不同 setAdapter(ListAdapter) 但可能是 WrapperListAdapter。

              【讨论】:

              • 我也有类似的问题。你能建议如何解决它。
              • 如果您查看上面的 2 个代码示例,您将看到 ListView 中的 mAdapter 和 ListView (AdapterView) 的父级中的 getAdapter()。如果重写 getAdapter(),getAdapter() 的结果可能不是 mAdapter。如果 2 个 Adapter 的计数不同,则会出现错误。
              【解决方案13】:

              我的问题与使用 Filter 和 ListView 有关。

              在设置或更新 ListView 的底层数据模型时,我在做这样的事情:

              public void updateUnderlyingContacts(List<Contact> newContacts, String filter)
              {
                  this.allContacts = newContacts;
                  this.filteredContacts = newContacts;
                  getFilter().filter(filter);
              }
              

              在最后一行调用filter() 将(并且必须)导致在过滤器的publishResults() 方法中调用notifyDataSetChanged()。有时这可能工作得很好,特别是在我快速的 Nexus 5 中。但实际上,它隐藏了一个错误,您会在速度较慢的设备或资源密集型条件下注意到该错误。

              问题是过滤是异步完成的,因此在filter()语句结束和对publishResults()的调用之间,都在UI线程中,其他一些UI线程代码可能会执行并更改内容适配器。

              实际修复很简单,只需在请求执行过滤之前也调用notifyDataSetChanged()

              public void updateUnderlyingContacts(List<Contact> newContacts, String filter)
              {
                  this.allContacts = newContacts;
                  this.filteredContacts = newContacts;
                  notifyDataSetChanged(); // Fix
                  getFilter().filter(filter);
              }
              

              【讨论】:

                【解决方案14】:

                如果 Feed 对象,我有一个列表。 它是从非 UI 线程附加和截断的。 它适用于下面的适配器。 无论如何,我在 UI 线程中调用 FeedAdapter.notifyDataSetChanged,但稍晚一些。 我之所以喜欢这样,是因为即使 UI 已死,我的 Feed 对象也会保留在本地服务的内存中。

                public class FeedAdapter extends BaseAdapter {
                    private int size = 0;
                    private final List<Feed> objects;
                
                    public FeedAdapter(Activity context, List<Feed> objects) {
                        this.context = context;
                        this.objects = objects;
                        size = objects.size();
                    }
                
                    public View getView(int position, View convertView, ViewGroup parent) {
                        ...
                    }
                
                    @Override
                    public void notifyDataSetChanged() {
                        size = objects.size();
                
                        super.notifyDataSetChanged();
                    }
                
                    @Override
                    public int getCount() {
                        return size;
                    }
                
                    @Override
                    public Object getItem(int position) {
                        try {
                            return objects.get(position);
                        } catch (Error e) {
                            return Feed.emptyFeed;
                        }
                    }
                
                    @Override
                    public long getItemId(int position) {
                        return position;
                    }
                }
                

                【讨论】:

                  【解决方案15】:

                  我在惰性图像加载器中添加新数据时遇到了同样的问题 我只是放了

                           adapter.notifyDataSetChanged();
                  

                         protected void onPostExecute(Void args) {
                          adapter.notifyDataSetChanged();
                          // Close the progressdialog
                          mProgressDialog.dismiss();
                           }
                  

                  希望对你有帮助

                  【讨论】:

                    【解决方案16】:

                    就像@Mullins 说的“
                    我都添加了这些项目并在 UI 线程中调用了notifyDataSetChanged(),我解决了这个问题。 ——穆林斯”。

                    就我而言,我有asynctask,我在doInBackground() 方法中调用了notifyDataSetChanged(),当我从onPostExecute() 调用时,我收到了异常。

                    【讨论】:

                      【解决方案17】:

                      这是 Android 4 到 4.4(KitKat) 中的一个已知错误,已在“>4.4”中解决

                      请看这里:https://code.google.com/p/android/issues/detail?id=71936

                      【讨论】:

                        【解决方案18】:

                        我有一个自定义的ListAdapter,并且在方法的开头而不是结尾调用super.notifyDataSetChanged()

                        @Override
                        public void notifyDataSetChanged() {
                            recalculate();
                            super.notifyDataSetChanged();
                        }
                        

                        【讨论】:

                          【解决方案19】:

                          我也遇到了完全相同的错误并使用 AsyncTask :

                          `java.lang.IllegalStateException:` The content of the adapter has changed but ListView  did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131296334, class android.widget.ListView) with Adapter... etc
                          

                          我通过将adapter.notifyDataSetChanged(); 放在我的 UI 线程的底部来解决它,这就是我的 AsyncTask onPostExecute 方法。像这样:

                           protected void onPostExecute(Void aVoid) {
                          
                           all my other stuff etc...
                              all my other stuff etc...
                          
                                     adapter.notifyDataSetChanged();
                          
                                          }
                          
                                      });
                                  }
                          

                          现在我的应用可以运行了。

                          编辑:事实上,我的应用程序仍然大约每 10 次崩溃一次,并给出相同的错误。

                          最后我在之前的帖子中遇到了runOnUiThread,我认为这可能很有用。所以我把它放在我的 doInBackground 方法中,像这样:

                          @Override
                          protected Void doInBackground(Void... voids) {
                          
                              runOnUiThread(new Runnable() {
                                                public void run() { etc... etc...
                          

                          我删除了adapter.notifyDataSetChanged(); 方法。现在,我的应用程序永远不会崩溃。

                          【讨论】:

                            【解决方案20】:

                            我遇到了类似的问题,这就是我的解决方法。我验证task 是否已经是RUNNINGFINISHED,因为一个任务只能运行一次。您将在下面看到我的解决方案中的部分改编代码。

                            public class MyActivity... {
                                private MyTask task;
                            
                                @Override
                                protected void onCreate(Bundle savedInstanceState) {
                                   // your code
                                   task = new MyTask();
                                   setList();
                                }
                            
                                private void setList() {
                                if (task != null)
                                    if (task.getStatus().equals(AsyncTask.Status.RUNNING)){
                                        task.cancel(true);
                                        task = new MyTask();
                                        task.execute();         
                                    } else if (task.getStatus().equals(AsyncTask.Status.FINISHED)) {
                                        task = new MyTask();
                                        task.execute();
                                    } else 
                                        task.execute();
                                }
                            
                                class MyTask extends AsyncTask<Void, Item, Void>{
                                   List<Item> Itens;
                            
                                   @Override
                                   protected void onPreExecute() {
                            
                                    //your code
                            
                                    list.setVisibility(View.GONE);
                                    adapterItem= new MyListAdapter(MyActivity.this, R.layout.item, new ArrayList<Item>());
                                    list.setAdapter(adapterItem);
                            
                                    adapterItem.notifyDataSetChanged();
                                }
                            
                                @Override
                                protected Void doInBackground(Void... params) {
                            
                                    Itens = getItens();
                                    for (Item item : Itens) {
                                        publishProgress(item );
                                    }
                            
                                    return null;
                                }
                            
                                @Override
                                protected void onProgressUpdate(Item ... item ) {           
                                    adapterItem.add(item[0]);
                                }
                            
                                @Override
                                protected void onPostExecute(Void result) {
                                    //your code
                                    adapterItem.notifyDataSetChanged();     
                                    list.setVisibility(View.VISIBLE);
                                }
                            
                            }
                            
                            }
                            

                            【讨论】:

                              【解决方案21】:

                              我有同样的情况,我在 listview 上的项目中有很多按钮组,并且我正在更改我的项目中的一些布尔值,例如 holder.rbVar.setOnclik...

                              我的问题发生是因为我在 getView(); 中调用了一个方法;并在 sharepreference 中保存了一个对象,所以我在上面遇到了同样的错误

                              我是如何解决的;我将 getView() 中的方法删除到 notifyDataSetInvalidated() 并且问题消失了

                                 @Override
                                  public void notifyDataSetChanged() {
                                      saveCurrentTalebeOnShare(currentTalebe);
                                      super.notifyDataSetChanged();
                                  }
                              

                              【讨论】:

                                【解决方案22】:

                                几天前我遇到了同样的问题,每天导致数千次崩溃,大约 0.1% 的用户遇到这种情况。我试过setVisibility(GONE/VISIBLE)requestLayout(),但崩溃次数只减少了一点。

                                我终于解决了。与setVisibility(GONE/VISIBLE) 无关。 requestLayout() 什么都没有。

                                最后我发现原因是我在更新数据后使用Handler调用notifyDataSetChanged(),这可能导致一种:

                                1. 将数据更新到模型对象(我称之为数据源)
                                2. 用户触摸列表视图(可能调用checkForTap()/onTouchEvent(),最后调用layoutChildren()
                                3. 适配器从模型对象中获取数据并调用notifyDataSetChanged()并更新视图

                                我又犯了一个错误,在getCount()getItem()getView() 中,我直接使用DataSource 中的字段,而不是将它们复制到适配器。所以最终它在以下情况下崩溃:

                                1. 适配器更新上次响应给出的数据
                                2. 当下次响应返回时,DataSource 更新数据,导致项目计数发生变化
                                3. 用户触摸列表视图,可能是点击、移动或翻转
                                4. 调用getCount()getView(),listview发现数据不一致,抛出java.lang.IllegalStateException: The content of the adapter has changed but...之类的异常。如果您在ListView 中使用页眉/页脚,另一个常见的例外是IndexOutOfBoundException

                                所以解决方案很简单,当我的 Handler 触发适配器获取数据并调用 notifyDataSetChanged() 时,我只需将数据从我的 DataSource 复制到适配器。现在崩溃再也不会发生了。

                                【讨论】:

                                • 这是我的问题。在我的情况下, getCount() 在调用 notifyDataSetChanged 之前返回可用的新计数。由于我的列表是“虚拟的”,我现在在我的 notifyDataSetChanged 方法中捕获更新的计数,然后在通过 getCount() 请求计数时返回这个缓存的计数。
                                【解决方案23】:

                                我有同样的问题。终于找到解决办法了

                                在更新列表视图之前,如果存在软键盘,请先将其关闭。之后设置数据源并调用 notifydatasetchanged()。

                                在内部关闭键盘时,listview 将更新其 ui。它一直在打电话,直到关闭键盘。那个时候如果数据源改变它会抛出这个异常。 如果 onActivityResult 中的数据正在更新,则可能会出现相同的错误。

                                 InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
                                            imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
                                
                                        view.postDelayed(new Runnable() {
                                            @Override
                                            public void run() {
                                                refreshList();
                                            }
                                        },100L);
                                

                                【讨论】:

                                  【解决方案24】:

                                  我的解决方案:

                                  1) 创建一个temp ArrayList

                                  2) 在doInBackground 方法中完成繁重的工作(sqlite 获取行,...)并将项目添加到临时数组列表。

                                  3) 以onPostExecute 方法将临时数组列表中的所有项目添加到列表视图的数组列表中。

                                  note: 你可能想从 listview 中删除一些项目,并从 sqlite 数据库中删除,也许从 sdcard 中删除一些与项目相关的文件,只需从数据库中删除项目并删除它们的相关文件并将它们添加到 @ 中的临时数组列表987654325@。然后在UI thread 从列表视图的数组列表中删除临时数组列表中存在的项目。

                                  希望这会有所帮助。

                                  【讨论】:

                                    【解决方案25】:
                                    adapter.notifyDataSetChanged()
                                    

                                    【讨论】:

                                    • 在 Stack Overflow 上添加解释说明为什么您的解决方案应该有效或优于现有解决方案是一种很好的做法。更多信息请阅读How To Answer
                                    猜你喜欢
                                    • 1970-01-01
                                    • 2011-09-22
                                    • 2014-12-26
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    • 1970-01-01
                                    相关资源
                                    最近更新 更多