Android,ListView IllegalStateException:“适配器的内容已经改变,但ListView没有收到通知”

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

我知道我必须避免 :我不能混淆来自后台线程的ListAdapter内容,所以我inheritance了AsyncTask并从onProgressUpdate发布结果(将条目添加到适配器)。 我的适配器使用结果对象的ArrayList,这些数组列表上的所有操作都是同步的。

其他人的研究 :这里有非常有价值的数据。 我也遇到了几乎每天大约500人的崩溃,当我在onProgressUpdate中添加list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)块时,崩溃降低了10倍,但没有消失。 (有人build议回答 )

我有时得到的是 :请注意,这真的很less发生(一个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) [...] 

帮帮我? 不再需要,见下文

最终答案:事实certificate,我每5次插入一次就调用notifyDataSetChanged以避免闪烁和突然的列表更改。 不能这样做,总是在基表发生变化时通知适配器。 这个bug现在已经过去了。

我遇到过同样的问题。

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

解决scheme:我已经做了两个, adding the items并在UI线程中调用notifyDataSetChanged()

我有同样的问题,但我使用该方法修复它

 requestLayout(); 

来自类ListView

这是一个多线程问题和使用正确同步的块这可以防止。 不要在UI线程上添加额外的东西,导致应用程序的响应速度的损失。

我也面临同样的。 而最接受的答案build议从UI线程更改适配器数据可以解决这个问题。 这将工作,但是一个快速和容易的解决scheme,但不是最好的。

正如你可以看到一个正常的情况。 从后台线程更新数据适配器并在UI线程中调用notifyDataSetChanged工作。

这个illegalStateException在UI线程更新视图时出现,而另一个后台线程再次更改数据。 那一刻导致了这个问题。

因此,如果您将同步更改适配器数据并进行notifydatasetchange调用的所有代码。 这个问题应该消失了。 由于我了,我仍然从后台线程更新数据。

这是我的案件特定的代码供他人参考。

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

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

这PhoneBookManager.getPhoneBookContacts从电话簿读取联系人,并填充他们在hashmaps。 它可以直接用于列表适配器绘制列表。

我的屏幕上有一个button。 这将打开列出这些电话号码的活动。 如果我直接在上一个线程完成其工作,这是快速naviagtion情况下发生的工作之前列表直接设置ADAPTER更less发生。 它popup了exception。这是这个问题的标题。 所以我必须在第二项活动中做这样的事情。

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

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

这解决了我的问题。

这段代码只是一个片段。 你需要改变它来为你编译。

 @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()); } } } 

希望这可以帮助

我解决了这个有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(); } }); } 

我写了这段代码,并运行在一个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); } } 

我的问题是与ListView一起使用filter

当设置或更新ListView的基础数据模型时,我正在做这样的事情:

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

在最后一行调用filter()将(并且必须)导致notifyDataSetChanged()在filter的publishResults()方法中被调用。 有时这可能会有用,特别是在我的快速Nexus 5中。但实际上,它隐藏了一个bug,您会发现在较慢的设备或资源密集的条件下。

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

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

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

如果Feed对象有一个List。 它被附加并从非UI线程截断。 它适用于下面的适配器。 无论如何,我FeedAdapter.notifyDataSetChanged在UI线程中调用FeedAdapter.notifyDataSetChanged 。 我这样做是因为我的Feed对象留在本地服务的内存中,即使UI已经死了。

 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; } } 

我正在面对同样的问题,完全相同的错误日志。 在我的情况下,AsyncTask的onProgress()使用mAdapter.add(newEntry)将值添加到适配器。 为了避免UI变得不太响应,我设置了mAdapter.setNotifyOnChange(false)并且每秒调用mAdapter.notifyDataSetChanged()四次。 每秒一次数组sorting。

这工作良好,看起来很容易上瘾,但不幸的是,它可以通过触摸所显示的列表项足够频繁地崩溃。

但似乎我find了一个可以接受的解决办法。 我的猜测是,即使你只是在ui线程上工作,适配器也不会调用notifyDataSetChanged()来接受数据的很多改变,因为这样我创build了一个存储所有新项目的队列,直到提到的300ms结束。 如果这个时刻到达,我将所有存储的项目添加到一个镜头中,并调用notifyDataSetChanged() 。 到目前为止,我无法再将这个列表弄坏

这是Android 4.4至4.4(KitKat)中的一个已知错误,并在“> 4.4”

请参阅https://code.google.com/p/android/issues/detail?id=71936

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

前几天我遇到了同样的问题,每天造成几千次的崩溃,大约有0.1%的用户遇到这种情况。 我尝试了setVisibility(GONE/VISIBLE)requestLayout() ,但是碰撞计数只是略有减less。

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

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

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

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

  1. 适配器更新最后响应给出的数据
  2. 当下一个响应返回时,DataSource会更新数据,这将导致项目数量更改
  3. 用户触摸listview,这可能是一个轻拍或移动或翻转
  4. getCount()getView()被调用,而listview发现数据不一致,并抛出像java.lang.IllegalStateException: The content of the adapter has changed but...exceptionjava.lang.IllegalStateException: The content of the adapter has changed but... 如果您在ListView使用页眉/页脚,另一个常见exception是IndexOutOfBoundException

所以解决scheme很简单,当我的Handler触发适配器来获取数据并调用notifyDataSetChanged()时,我只是从DataSource notifyDataSetChanged()数据复制到适配器。 现在的崩溃再也没有发生过。

如果间歇性地发生这种情况,事实certificate,当点击“加载更多”最后一个项目后滚动列表时,我只有这个问题。 如果列表没有滚动,一切工作正常。

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

validation发生时,此代码在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(); 

注意Adapter不能保证是一样的!

在我的情况下,因为它是一个“LoadMoreAdapter”,我在getAdapter调用中返回WrappedAdapter(用于访问基础对象)。 这导致计数是不同的,因为额外的“加载更多”项目和抛出exception。

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

ListView.getAdapter javadoc

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

我有同样的问题,我解决了。 我的问题是,我正在使用一个listview ,一个数组适配器和filter。 在方法performFiltering我搞砸了有数据的数组,因为这个方法没有运行在UI线程和EVENTUALLY它引发了一些问题。

造成这种崩溃的一个原因是ArrayList对象不能完全改变。 所以,当我删除一个项目,我必须这样做:

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

这确定了我的崩溃。

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

我也得到完全相同的错误,并使用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次坠毁约1次,给出同样的错误。

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

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

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

我遇到了类似的问题,这里是我解决我的情况。 我validationtask已经RUNNING或完成,因为任务只能运行一次。 下面你会看到我的解决scheme中的部分和改编的代码。

 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); } } } 

请尝试以下解决scheme之一:

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

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

当我在懒惰的图像加载器中添加新的数据时,我有同样的问题

  adapter.notifyDataSetChanged(); 

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

希望它可以帮助你

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

在我的情况下,我有asynctask ,我在doInBackground()方法中调用了notifyDataSetChanged() ,问题解决了,当我从onPostExecute()调用我收到exception。

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

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

我有同样的sittuation,我有很多buttongroup insite我的项目上的listview,我正在改变一些布尔值在我的项目里面像holder.rbVar.setOnclik …

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

我如何解决它; 我删除了我的方法getView()中notifyDataSetInvalidated()和问题消失了

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

我有同样的问题。 最后我得到了解决scheme

在更新列表视图之前,如果软键盘存在,首先closures它。 之后设置数据源并调用notifydatasetchanged()。

同时closures键盘内部列表视图将更新其用户界面。 它保持通话直到closures键盘。 那个时候如果数据源发生变化就会抛出这个exception。 如果数据在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);