Android, ListView IllegalStateException: “Содержание адаптера изменилось, но ListView не получил уведомление”

Что я хочу сделать: выполните фоновый поток, который вычисляет содержание 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 вставок, чтобы не мерцать и внезапные изменения списка. Это не может быть сделано такой путь, всегда уведомлять адаптер, когда основа перечисляет изменения. Эта ошибка это долго уводимый для меня теперь.

187
задан Sandip Armal Patil 5 February 2015 в 20:31
поделиться

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);
    }
}
7
ответ дан 23 November 2019 в 05:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: