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”
即使我在XMPP通知应用程序中面临同样的问题,接收者消息需要被添加回列表视图(用ArrayList
实现)。 当我尝试通过MessageListener
(单独的线程)添加接收者内容时,应用程序退出时出现上述错误。 我通过将runOnUiThread
方法(它是Activity类的一部分)添加到我的arraylist
& setListviewadapater
来解决这个问题。 这解决了我的问题。
前几天我遇到了同样的问题,每天造成几千次的崩溃,大约有0.1%的用户遇到这种情况。 我尝试了setVisibility(GONE/VISIBLE)
和requestLayout()
,但是碰撞计数只是略有减less。
我终于解决了。 没有什么与setVisibility(GONE/VISIBLE)
。 requestLayout()
没有。
最后我发现原因是我使用一个Handler
来更新数据后调用notifyDataSetChanged()
,这可能导致一种:
- 将数据更新到模型对象(我称之为数据源)
- 用户触摸listview(可能调用
checkForTap()
/onTouchEvent()
,最后调用layoutChildren()
) - 适配器从模型对象获取数据并调用
notifyDataSetChanged()
并更新视图
而且我在getCount()
, getItem()
和getView()
犯了另一个错误,我直接使用DataSource中的字段,而不是将它们复制到适配器。 所以最后它崩溃的时候:
- 适配器更新最后响应给出的数据
- 当下一个响应返回时,DataSource会更新数据,这将导致项目数量更改
- 用户触摸listview,这可能是一个轻拍或移动或翻转
-
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之一:
-
有时,如果将新对象添加到线程(或
doInBackground
方法)中的数据列表中,则会发生此错误。 解决方法是:创build一个临时列表,并在线程(或doInBackground
)中向该列表中添加数据,然后将所有数据从临时列表复制到UI线程(或onPostExcute
)中的适配器列表中 -
确保在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);
- Javascriptexception堆栈跟踪
- 我如何捕捉像是一个exception(不只是为了testing)的一个numpy警告?
- 为什么是exception.printStackTrace()被认为是不好的做法?
- 将多个文件添加到目录时,FileSystemWatcher出现文件访问错误
- Asp.net mvc覆盖基本控制器中的OnException一直传播到Application_Error
- 如何防止后台线程中的exception终止应用程序?
- 鉴于最后的块没有正确填充
- 我如何在Rails中引发exception,使其行为像其他Railsexception?
- locking的对象是否locking,如果内部发生exception?