Что я хочу сделать: выполните фоновый поток, который вычисляет содержание ListView и обновление ListView частично, в то время как результаты вычисляются.
Что я знаю, что должен избежать: Я не могу смешать с содержанием ListAdapter от фонового потока, таким образом, я наследовал AsyncTask, и опубликуйте результат (добавьте записи в адаптер) от onProgressUpdate. Мой Адаптер использует ArrayList объектов результата, все операции на тех arraylists синхронизируются.
Исследование других людей: здесь существуют очень ценные данные. Я также пострадал почти от ежедневных катастрофических отказов для группы из ~500 пользователей, и когда я добавил list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)
блок в onProgressUpdate, катастрофические отказы, пониженные фактором 10, но не, исчез. (это было предложено в ответе),
Что я иногда получал: заметьте, это происходит действительно редко (один раз в неделю для одного из 3.5k пользователи). Но я хотел бы избавиться от этой ошибки полностью. Вот частичный stacktrace:
`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)
[...]
Помощь? Не нужный больше, посмотрите ниже
ОКОНЧАТЕЛЬНЫЙ ОТВЕТ: Как оказалось, я звонил notifyDataSetChanged
каждые 5 вставок, чтобы не мерцать и внезапные изменения списка. Это не может быть сделано такой путь, всегда уведомлять адаптер, когда основа перечисляет изменения. Эта ошибка это долго уводимый для меня теперь.
Я написал этот код, и он работал в образе эмулятора 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);
}
}