Что я хочу сделать : запустить фоновый поток, который вычисляет содержимое ListView и частично обновлять ListView, пока вычисляются результаты.
Я знаю, что мне следует избегать : я не могу связываться с содержимым ListAdapter из фонового потока, поэтому я унаследовал AsyncTask и опубликовал результат (добавление записей в адаптер) из onProgressUpdate. Мой адаптер использует ArrayList объектов результатов, все операции над этими массивами синхронизируются.
Исследование других людей : есть очень ценные данные здесь . Я также страдал от почти ежедневных сбоев для группы из ~ 500 пользователей, и когда я добавил list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)
блок в onProgressUpdate, сбои уменьшились в 10 раз, но не исчезли. (это было предложено в ответе )
Что я иногда получаю : обратите внимание, это случается очень редко (раз в неделю для одного из пользователей 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)
[...]
Помогите? Не нужно больше, см. Ниже
ЗАКЛЮЧИТЕЛЬНЫЙ ОТВЕТ: Как оказалось, я звонил notifyDataSetChanged
каждые 5 вставок, чтобы избежать мерцания и внезапных изменений в списке. Это не может быть сделано таким образом, всегда уведомляйте адаптер при изменении базового списка. Эта ошибка давно исчезла для меня.
Ответы:
Я была такая же проблема.
Я добавлял предметы в мой
ArrayList
внешний поток.Решение: я сделал оба,
adding the items
и вызвалnotifyDataSetChanged()
в потоке пользовательского интерфейса.источник
У меня была такая же проблема, но я исправил ее методом
из класса
ListView
источник
Это проблема многопоточности и использование правильно синхронизированных блоков. Это можно предотвратить. Не добавляя лишних вещей в UI Thread и не вызывая потери отзывчивости приложения.
Я тоже сталкивался с тем же. И поскольку наиболее приемлемый ответ предлагает внести изменения в данные адаптера из потока пользовательского интерфейса, можно решить эту проблему. Это будет работать, но это быстрое и простое решение, но не лучшее.
Как вы можете видеть для нормального случая. Обновление адаптера данных из фонового потока и вызов notifyDataSetChanged в потоке пользовательского интерфейса работает.
Это invalidStateException возникает, когда поток пользовательского интерфейса обновляет представление, а другой фоновый поток снова изменяет данные. Этот момент вызывает эту проблему.
Так что если вы будете синхронизировать весь код, который изменяет данные адаптера и делает вызов notifydatasetchange. Эта проблема должна исчезнуть. Как прошло для меня, и я все еще обновляю данные из фонового потока.
Вот мой конкретный код для других пользователей.
Мой загрузчик на главном экране загружает контакты телефонной книги в мои источники данных в фоновом режиме.
Этот PhoneBookManager.getPhoneBookContacts считывает контакты из телефонной книги и заполняет их в хэш-картах. Который непосредственно используется для списочных адаптеров для составления списка.
На моем экране есть кнопка. Это открывает деятельность, в которой перечислены эти номера телефонов. Если я непосредственно устанавливаю Adapter над списком до того, как предыдущий поток завершит свою работу, что является быстрым случаем навигации, случается реже. Появляется исключение. Это название этого ТАКОГО вопроса. Поэтому я должен сделать что-то подобное во втором упражнении.
Мой загрузчик во втором задании ожидает завершения первого потока. Пока это показывает индикатор выполнения. Проверьте loadInBackground обоих загрузчиков.
Затем он создает адаптер и доставляет его деятельности, где в потоке пользовательского интерфейса я вызываю setAdapter.
Это решило мою проблему.
Этот код только фрагмент. Вы должны изменить это, чтобы компилировать хорошо для вас.
Надеюсь это поможет
источник
Я решил это с помощью 2 списков. Один список я использую только для адаптера, и я делаю все изменения / обновления данных в другом списке. Это позволяет мне обновлять один список в фоновом потоке, а затем обновлять список «адаптеров» в главном потоке / пользовательском интерфейсе:
источник
Я написал этот код, и он работал в образе эмулятора 2.1 в течение ~ 12 часов и не получил исключение IllegalStateException. Я собираюсь дать фреймворку для Android преимущество сомнения и скажу, что это, скорее всего, ошибка в вашем коде. Надеюсь, это поможет. Может быть, вы можете адаптировать его к вашему списку и данным.
источник
Несколько дней назад я столкнулся с той же самой проблемой и вызывает несколько тысяч сбоев в день, примерно 0,1% пользователей встречают эту ситуацию. Я пытался
setVisibility(GONE/VISIBLE)
иrequestLayout()
, но количество аварий только немного уменьшается.И я наконец решил это. Ничего с
setVisibility(GONE/VISIBLE)
. Ничего сrequestLayout()
.Наконец, я нашел причину в том, что я использовал
Handler
для вызоваnotifyDataSetChanged()
после обновления данных, что может привести к некоторому виду:checkForTap()
/onTouchEvent()
и, наконец, звонкиlayoutChildren()
)notifyDataSetChanged()
и обновляет представленияИ я сделал еще одну ошибку , что
getCount()
,getItem()
иgetView()
я непосредственно использовать поля в DataSource, а не копировать их к адаптеру. Так что, наконец, он падает, когда:getCount()
иgetView()
вызывается, и listview обнаруживает, что данные не согласованы, и выдает исключения вродеjava.lang.IllegalStateException: The content of the adapter has changed but...
. Другое распространенное исключение -IndexOutOfBoundException
если вы используете верхний / нижний колонтитул вListView
.Таким образом, решение легко, я просто копирую данные в адаптер из моего источника данных, когда мой обработчик запускает адаптер для получения данных и вызовов
notifyDataSetChanged()
. Крушение теперь никогда не случится снова.источник
Если бы это происходило с перебоями, оказалось, что эта проблема возникла у меня только тогда, когда список прокручивался после того, как был нажат последний элемент «загрузить еще». Если список не прокручивался, все работало нормально.
После ОЧЕНЬ отладки это была ошибка с моей стороны, но несоответствие в коде Android также.
Когда проверка происходит, этот код выполняется в ListView
Но когда происходит onChange, он запускает этот код в AdapterView (родитель ListView)
Обратите внимание, что адаптер НЕ гарантированно одинаков!
В моем случае, поскольку это был «LoadMoreAdapter», я возвращал WrappedAdapter в вызове getAdapter (для доступа к базовым объектам). Это привело к тому, что счетчики стали другими из-за дополнительного элемента «Загрузить еще» и выданного исключения.
Я сделал это только потому, что из документов видно, что это нормально
ListView.getAdapter Javadoc
источник
Моя проблема была связана с использованием фильтра вместе с ListView.
При настройке или обновлении базовой модели данных ListView я делал что-то вроде этого:
призвание
filter()
в последней строке вызовет (и должен) вызвать методnotifyDataSetChanged()
в фильтреpublishResults()
. Иногда это может работать нормально, особенно в моем быстром Nexus 5. Но на самом деле, он скрывает ошибку, которую вы заметите на более медленных устройствах или в ресурсоемких условиях.Проблема в том, что фильтрация выполняется асинхронно, и, следовательно, между концом
filter()
оператора и вызовомpublishResults()
, как в потоке пользовательского интерфейса, может выполняться какой-то другой код потока пользовательского интерфейса и изменять содержимое адаптера.Фактическое исправление легко, просто позвоните
notifyDataSetChanged()
также, прежде чем запрашивать выполнение фильтрации:источник
У меня есть список объектов Feed. Он добавляется и усекается из потока без пользовательского интерфейса. Он отлично работает с адаптером ниже. В
FeedAdapter.notifyDataSetChanged
любом случае, я звоню в теме интерфейса, но чуть позже. Мне это нравится, потому что мои объекты Feed остаются в памяти в Local Service, даже когда пользовательский интерфейс не работает.источник
Я столкнулся с той же проблемой с точно таким же журналом ошибок. В моем случае
onProgress()
AsyncTask добавляет значения к адаптеру, используяmAdapter.add(newEntry)
. Чтобы пользовательский интерфейс стал менее отзывчивым, я устанавливаюmAdapter.setNotifyOnChange(false)
и звонюmAdapter.notifyDataSetChanged()
4 раза во второй раз. Раз в секунду массив сортируется.Это хорошо работает и выглядит очень затягивающе, но, к сожалению, его можно разбить, достаточно часто касаясь показанных пунктов списка.
Но, похоже, я нашел приемлемый обходной путь. Я предполагаю, что даже если вы просто работаете в потоке пользовательского интерфейса, адаптер не принимает много изменений в своих данных без вызова
notifyDataSetChanged()
, из-за этого я создал очередь, в которой хранятся все новые элементы до тех пор, пока не закончатся упомянутые 300 мс. Если этот момент достигнут, я добавляю все сохраненные предметы в один выстрел и вызываюnotifyDataSetChanged()
. До сих пор я не был в состоянии разрушить список больше .источник
Это известная ошибка в Android 4 до 4.4 (KitKat), которая устраняется в «> 4.4»
Смотрите здесь: https://code.google.com/p/android/issues/detail?id=71936.
источник
Даже если я столкнулся с той же проблемой в моем приложении уведомлений XMPP, сообщение получателей необходимо добавить обратно в представление списка (реализовано с помощью
ArrayList
). Когда я попытался добавить содержимое получателя черезMessageListener
(отдельный поток), приложение закрывается с ошибкой выше. Я решил эту проблему, добавив содержание к моейarraylist
иsetListviewadapater
поrunOnUiThread
методу , который является частью класса Activity. Это решило мою проблему.источник
Я столкнулся с подобной проблемой, вот как я решил в моем случае. Я проверяю, есть ли
task
ужеRUNNING
илиFINISHED
потому что задача может быть запущена только один раз. Ниже вы увидите частичный и адаптированный код из моего решения.источник
У меня была такая же проблема, и я решил ее. Моя проблема заключалась в том, что я использовал
listview
, с адаптером массива и с фильтром. В методе, которыйperformFiltering
я использовал для работы с массивом, в котором есть данные, и это была проблема, так как этот метод не работает в потоке пользовательского интерфейса и в конечном итоге вызывает некоторые проблемы.источник
Одной из причин этого сбоя является то, что
ArrayList
объект не может полностью измениться. Итак, когда я удаляю элемент, я должен сделать это:Это исправило крах для меня.
источник
В моем случае я вызвал метод
GetFilter()
на адаптере изTextWatcher()
метода основной операции и добавил данные с включенным циклом ForGetFilter()
. Решением было изменить цикл For наAfterTextChanged()
метод sub в основной Activity и удалить вызовGetFilter()
источник
источник
Я также получаю точно такую же ошибку и использую AsyncTask:
Я решил это, поместив
adapter.notifyDataSetChanged();
внизу мой поток пользовательского интерфейса, то есть метод AsyncTask onPostExecute. Как это :Теперь мое приложение работает.
РЕДАКТИРОВАТЬ: На самом деле, мое приложение по-прежнему сбой примерно каждые 1 в 10 раз, выдавая ту же ошибку.
В конце концов я наткнулся
runOnUiThread
на предыдущий пост, который, по моему мнению, мог бы быть полезным. Поэтому я поместил его в мой метод doInBackground, например так:И я удалил
adapter.notifyDataSetChanged();
метод. Теперь мое приложение никогда не падает.источник
Пожалуйста, попробуйте одно из этих решений:
Иногда, если вы добавляете новый объект в список данных в потоке (или
doInBackground
методе), эта ошибка возникает. Решение: создайте временный список и добавьте данные в этот список в потоке (илиdoInBackground
), затем скопируйте все данные из временного списка в список адаптеров в потоке пользовательского интерфейса (илиonPostExcute
).Убедитесь, что все обновления пользовательского интерфейса вызываются в потоке пользовательского интерфейса.
источник
У меня была та же проблема, когда я добавляю новые данные в загрузчик ленивых изображений, я просто положил
в
надеюсь, это поможет вам
источник
Как сказал @Mullins: «
Я добавил элементы и вызвал их
notifyDataSetChanged()
в ветке пользовательского интерфейса, и я решил это. - Маллинс».В моем случае у меня есть ,
asynctask
и я позвонилnotifyDataSetChanged()
вdoInBackground()
методе , и проблема будет решена, когда я звонил изonPostExecute()
я получил исключение.источник
У меня был обычай,
ListAdapter
и я звонилsuper.notifyDataSetChanged()
в начале, а не в конце методаисточник
У меня была та же самая ситуационная ситуация, у меня было много buttongroup, чтобы инсталировать свой элемент в списке, и я изменял некоторые логические значения внутри моего элемента, как holder.rbVar.setOnclik ...
моя проблема произошла, потому что я вызывал метод внутри getView (); и сохранял объект внутри sharepreference, поэтому у меня была та же ошибка выше
Как я это решил; Я удалил свой метод внутри getView () для notifyDataSetInvalidated () и проблема исчезла
источник
У меня такая же проблема. наконец я получил решение
перед обновлением списка, если программная клавиатура присутствует, сначала закройте ее. после этого установите источник данных и вызовите notifydatasetchanged ().
при внутреннем закрытии клавиатуры listview обновит свой интерфейс. он продолжает звонить до закрытия клавиатуры. тогда, если источник данных изменится, он выдаст это исключение. если данные обновляются в onActivityResult, существует вероятность возникновения такой же ошибки.
источник
Мое решение:
1) создать
temp ArrayList
.2) выполнять тяжелую работу (выборка строк sqlite, ...) в
doInBackground
Выполните методе и добавьте элементы в временный массив.3) добавить все элементы из временного архива в
onPostExecute
метод списка рассылки .note:
Вы можете удалить некоторые элементы из списка, а также удалить из базы данных sqlite и, возможно, удалить некоторые файлы, связанные с элементами из sdcard, просто удалить элементы из базы данных и удалить связанные с ними файлы и добавить их во временный массив вbackground thread
. затем вUI thread
удалить элементы, существующие в временном архиве из списка рассылки.Надеюсь это поможет.
источник