Ce que je veux faire : exécuter un thread d'arrière-plan qui calcule le contenu de ListView et met à jour partiellement ListView, pendant que les résultats sont calculés.
Ce que je sais que je dois éviter : je ne peux pas jouer avec le contenu de ListAdapter du thread d'arrière-plan, j'ai donc hérité d'AsyncTask et publié le résultat (ajouter des entrées à l'adaptateur) de onProgressUpdate. Mon adaptateur utilise ArrayList des objets de résultat, toutes les opérations sur ces arraylists sont synchronisées.
Recherche d'autres personnes : il y a ici des données très précieuses . J'ai également souffert de plantages presque quotidiens pour un groupe d'environ 500 utilisateurs, et lorsque j'ai ajouté un list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)
bloc dans onProgressUpdate, les plantages ont été réduits d'un facteur 10 mais n'ont pas disparu. (il a été suggéré en réponse )
Ce que j'ai parfois : remarquez, cela arrive très rarement (une fois par semaine pour l'un des 3,5k utilisateurs). Mais j'aimerais me débarrasser complètement de ce bug. Voici un stacktrace partiel:
`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)
[...]
Aidez-moi? Plus besoin, voir ci-dessous
RÉPONSE FINALE: Il s'est avéré que j'appelais notifyDataSetChanged
toutes les 5 insertions pour éviter le scintillement et les changements soudains de liste. Cela ne peut pas être fait de cette manière, avertissez toujours l'adaptateur lorsque la liste de base change. Ce bug est parti depuis longtemps pour moi maintenant.
Réponses:
J'ai eu le même problème.
J'ajoutais des éléments à mon
ArrayList
fil externe à l'interface utilisateur.Solution: j'ai fait les deux
adding the items
et j'ai appelénotifyDataSetChanged()
le fil d'interface utilisateur.la source
J'ai eu le même problème, mais je l'ai résolu en utilisant la méthode
de la classe
ListView
la source
Il s'agit d'un problème de multi - threading et l'utilisation de blocs correctement synchronisés peut être évitée. Sans mettre d'éléments supplémentaires sur UI Thread et entraîner une perte de réactivité de l'application.
J'ai également fait face à la même chose. Et comme la réponse la plus acceptée le suggère, apporter des modifications aux données de l'adaptateur à partir de UI Thread peut résoudre le problème. Cela fonctionnera, mais c'est une solution rapide et facile mais pas la meilleure.
Comme vous pouvez le voir pour un cas normal. La mise à jour de l'adaptateur de données à partir du thread d'arrière-plan et l'appel de notifyDataSetChanged dans le thread d'interface utilisateur fonctionnent.
Cette illégaleStateException survient lorsqu'un thread d'interface utilisateur met à jour la vue et qu'un autre thread d'arrière-plan modifie à nouveau les données. Ce moment provoque ce problème.
Donc, si vous synchronisez tout le code qui modifie les données de l'adaptateur et effectue un appel notifydatasetchange. Ce problème devrait disparaître. C'est parti pour moi et je suis toujours en train de mettre à jour les données du fil d'arrière-plan.
Voici mon code spécifique à mon cas pour que les autres puissent se référer.
Mon chargeur sur l'écran principal charge les contacts du répertoire téléphonique dans mes sources de données en arrière-plan.
Ce PhoneBookManager.getPhoneBookContacts lit les contacts du répertoire et les remplit dans les hashmaps. Ce qui est directement utilisable par les adaptateurs de liste pour dessiner une liste.
Il y a un bouton sur mon écran. Cela ouvre une activité où ces numéros de téléphone sont répertoriés. Si je place directement l'adaptateur sur la liste avant que le thread précédent ne termine son travail, le cas de navigation rapide se produit moins souvent. Il fait apparaître l'exception. Quel est le titre de cette question SO. Je dois donc faire quelque chose comme ça dans la deuxième activité.
Mon chargeur dans la deuxième activité attend la fin du premier thread. Jusqu'à ce qu'il affiche une barre de progression. Vérifiez le loadInBackground des deux chargeurs.
Ensuite, il crée l'adaptateur et le livre à l'activité où, sur le thread d'interface utilisateur, j'appelle setAdapter.
Cela a résolu mon problème.
Ce code n'est qu'un extrait de code. Vous devez le changer pour bien compiler pour vous.
J'espère que cela t'aides
la source
J'ai résolu ce problème en ayant 2 listes. Une liste que j'utilise uniquement pour l'adaptateur, et je fais toutes les modifications / mises à jour de données sur l'autre liste. Cela me permet de faire des mises à jour sur une liste dans un thread d'arrière-plan, puis de mettre à jour la liste "adaptateur" dans le thread principal / UI:
la source
J'ai écrit ce code et l'ai fait fonctionner dans une image d'émulateur 2.1 pendant ~ 12 heures et je n'ai pas obtenu l'exception IllegalStateException. Je vais donner au cadre Android le bénéfice du doute sur celui-ci et dire qu'il s'agit probablement d'une erreur dans votre code. J'espère que ça aide. Vous pouvez peut-être l'adapter à votre liste et à vos données.
la source
Il y a plusieurs jours j'ai rencontré le même problème et cause plusieurs milliers de plantages par jour, environ 0,1% des utilisateurs rencontrent cette situation. J'ai essayé
setVisibility(GONE/VISIBLE)
etrequestLayout()
, mais le nombre de crashs ne diminue qu'un peu.Et je l'ai finalement résolu. Rien avec
setVisibility(GONE/VISIBLE)
. Rien avecrequestLayout()
.Enfin, j'ai trouvé que la raison était que j'utilisais un
Handler
appelnotifyDataSetChanged()
après la mise à jour des données, ce qui peut conduire à une sorte de:checkForTap()
/onTouchEvent()
et enfin appelerlayoutChildren()
)notifyDataSetChanged()
et met à jour les vuesEt j'ai fait une autre erreur que dans
getCount()
,getItem()
etgetView()
, j'utilise directement des champs dans DataSource, plutôt que de les copier sur l'adaptateur. Alors finalement ça plante quand:getCount()
etgetView()
est appelé, et listview trouve que les données ne sont pas cohérentes et lève des exceptions telles quejava.lang.IllegalStateException: The content of the adapter has changed but...
. Une autre exception courante est uneIndexOutOfBoundException
utilisation de l'en-tête / pied de page dansListView
.La solution est donc simple, je copie simplement les données vers l'adaptateur à partir de ma source de données lorsque mon gestionnaire déclenche l'adaptateur pour obtenir des données et des appels
notifyDataSetChanged()
. Le crash ne se reproduit plus jamais.la source
Si cela se produisait par intermittence, il s'avère que je n'ai eu ce problème que lorsque la liste a été défilée après un dernier élément `` charger plus '' a été cliqué. Si la liste n'était pas défilée, tout fonctionnait bien.
Après BEAUCOUP de débogage, c'était un bug de ma part, mais une incohérence dans le code Android également.
Lorsque la validation se produit, ce code est exécuté dans ListView
Mais quand onChange se produit, il déclenche ce code dans AdapterView (parent de ListView)
Notez que l'adaptateur n'est PAS garanti d'être le même!
Dans mon cas, puisqu'il s'agissait d'un 'LoadMoreAdapter', je retournais le WrappedAdapter dans l'appel getAdapter (pour accéder aux objets sous-jacents). Cela a entraîné des décomptes différents en raison de l'élément supplémentaire `` Charger plus '' et de l'exception lancée.
Je n'ai fait cela que parce que les documents donnent l'impression que c'est correct
ListView.getAdapter javadoc
la source
Mon problème était lié à l'utilisation d'un filtre avec ListView.
Lors de la configuration ou de la mise à jour du modèle de données sous-jacent de ListView, je faisais quelque chose comme ceci:
L'appel
filter()
de la dernière ligne entraînera (et doit)notifyDataSetChanged()
être appelé dans lapublishResults()
méthode Filter . Cela peut parfois fonctionner correctement, spécialement dans mon Nexus 5 rapide. Mais en réalité, cela cache un bogue que vous remarquerez avec des appareils plus lents ou dans des conditions gourmandes en ressources.Le problème est que le filtrage est effectué de manière asynchrone, et donc entre la fin de l'
filter()
instruction et l'appel àpublishResults()
, tous deux dans le thread d'interface utilisateur, un autre code de thread d'interface utilisateur peut s'exécuter et modifier le contenu de l'adaptateur.Le correctif réel est simple, il suffit d'appeler
notifyDataSetChanged()
également avant de demander le filtrage à effectuer:la source
J'ai une liste des objets Feed. Il est ajouté et tronqué à partir d'un thread sans interface utilisateur. Cela fonctionne bien avec l'adaptateur ci-dessous. J'appelle
FeedAdapter.notifyDataSetChanged
de toute façon le fil d'interface utilisateur, mais un peu plus tard. J'aime cela car mes objets Feed restent en mémoire dans le service local même lorsque l'interface utilisateur est morte.la source
J'étais confronté au même problème avec exactement le même journal d'erreurs. Dans mon cas,
onProgress()
AsyncTask ajoute les valeurs à l'adaptateur en utilisantmAdapter.add(newEntry)
. Pour éviter que l'interface utilisateur ne devienne moins réactive, je configuremAdapter.setNotifyOnChange(false)
et appellemAdapter.notifyDataSetChanged()
4 fois la seconde. Une fois par seconde, le tableau est trié.Cela fonctionne bien et semble très addictif, mais malheureusement, il est possible de le planter en touchant assez souvent les éléments de la liste affichés.
Mais il semble que j'ai trouvé une solution de contournement acceptable. Je suppose que même si vous travaillez simplement sur le thread d'interface utilisateur, l'adaptateur n'accepte pas beaucoup de modifications de ses données sans appeler
notifyDataSetChanged()
, à cause de cela, j'ai créé une file d'attente qui stocke tous les nouveaux éléments jusqu'à ce que les 300 ms mentionnés soient terminés. Si ce moment est atteint, j'ajoute tous les éléments stockés en un seul coup et j'appellenotifyDataSetChanged()
. Jusqu'à présent, je ne pouvais plus écraser la liste .la source
Il s'agit d'un bogue connu dans Android 4 à 4.4 (KitKat) et est résolu dans "> 4.4"
Voir ici: https://code.google.com/p/android/issues/detail?id=71936
la source
Même si j'ai rencontré le même problème dans mon application de notification XMPP, le message des destinataires doit être ajouté à la vue de liste (implémenté avec
ArrayList
). Lorsque j'ai essayé d'ajouter le contenu du récepteur viaMessageListener
(thread séparé), l'application se ferme avec l'erreur ci-dessus. J'ai résolu ce problème en ajoutant le contenu à ma méthodearraylist
&setListviewadapater
throughrunOnUiThread
qui fait partie de la classe Activity. Cela a résolu mon problème.la source
J'ai fait face à un problème similaire, voici comment j'ai résolu mon cas. Je vérifie si c'est
task
déjà le casRUNNING
ouFINISHED
parce qu'une tâche ne peut s'exécuter qu'une seule fois. Ci-dessous, vous verrez un code partiel et adapté de ma solution.la source
J'ai eu le même problème et je l'ai résolu. Mon problème était que j'utilisais un
listview
, avec un adaptateur de tableau et avec un filtre. Sur la méthode,performFiltering
je jouais avec le tableau contenant les données et c'était le problème car cette méthode ne fonctionne pas sur le thread de l'interface utilisateur et, ÉVENTUELLEMENT, cela pose des problèmes.la source
L'une des causes de ce plantage est que l'
ArrayList
objet ne peut pas changer complètement. Donc, lorsque je supprime un élément, je dois faire ceci:Cela a corrigé le crash pour moi.
la source
Dans mon cas, j'ai appelé la méthode
GetFilter()
sur un adaptateur à partir de laTextWatcher()
méthode sur l'activité principale, et j'ai ajouté les données avec une boucle ForGetFilter()
. La solution était de changer la boucle For enAfterTextChanged()
sous-méthode sur l'activité principale et de supprimer l'appel àGetFilter()
la source
la source
J'obtenais également exactement la même erreur et j'utilisais AsyncTask:
Je l'ai résolu en mettant
adapter.notifyDataSetChanged();
au bas de mon fil d'interface utilisateur, c'est ma méthode AsyncTask onPostExecute. Comme ça :Maintenant, mon application fonctionne.
EDIT: En fait, mon application s'est toujours plantée environ une fois sur 10, donnant la même erreur.
Finalement, je suis tombé
runOnUiThread
sur un article précédent, qui, à mon avis, pourrait être utile. Alors je l'ai mis dans ma méthode doInBackground, comme ceci:Et j'ai supprimé la
adapter.notifyDataSetChanged();
méthode. Maintenant, mon application ne plante jamais.la source
Veuillez essayer l'une de ces solutions:
Parfois, si vous ajoutez un nouvel objet à la liste de données dans un thread (ou une
doInBackground
méthode), cette erreur se produit. La solution est: créer une liste temporaire et ajouter des données à cette liste dans le thread (oudoInBackground
), puis copier toutes les données de la liste temporaire vers la liste des adaptateurs dans le thread d'interface utilisateur (ouonPostExcute
)Assurez-vous que toutes les mises à jour de l'interface utilisateur sont appelées dans le fil de discussion de l'interface utilisateur.
la source
J'ai eu le même problème lors de l'ajout de nouvelles données dans le chargeur d'image paresseux que je viens de mettre
dans
j'espère que ça vous aide
la source
Comme @Mullins a dit: "
J'ai à la fois ajouté les éléments et appelé
notifyDataSetChanged()
dans le fil de l'interface utilisateur et j'ai résolu cela. - Mullins".Dans mon cas, j'ai
asynctask
et j'ai appelénotifyDataSetChanged()
ladoInBackground()
méthode et le problème est résolu, lorsque j'ai appelé deonPostExecute()
j'ai reçu l'exception.la source
J'avais une coutume
ListAdapter
et j'appelaissuper.notifyDataSetChanged()
au début et non à la fin de la méthodela source
J'avais la même situation, j'avais de nombreux groupes de boutons insérés dans mon élément sur listview et je changeais certaines valeurs booléennes à l'intérieur de mon élément comme holder.rbVar.setOnclik ...
mon problème est survenu parce que j'appelais une méthode dans getView (); et enregistrait un objet dans sharepreference, j'ai donc eu la même erreur ci-dessus
Comment je l'ai résolu; J'ai supprimé ma méthode dans getView () pour notifyDataSetInvalidated () et le problème a disparu
la source
J'ai eu le même problème. enfin j'ai la solution
avant de mettre à jour la liste, si le clavier virtuel est présent, fermez-le d'abord. après cela, définissez la source de données et appelez notifydatasetchanged ().
lors de la fermeture du clavier en interne, listview mettra à jour son interface utilisateur. il continue d'appeler jusqu'à la fermeture du clavier. cette fois, si la source de données change, cela lèvera cette exception. si les données sont mises à jour dans onActivityResult, il y a une chance pour la même erreur.
la source
Ma solution:
1) Créez un fichier
temp ArrayList
.2) faites vos gros travaux (sqlite row fetch, ...) dans la
doInBackground
méthode et ajoutez des éléments à la liste des temp.3) ajoutez tous les éléments de temp araylist à l'arraylist de votre listview dans
onPostExecute
method.note:
vous voudrez peut-être supprimer certains éléments de la vue de liste et également supprimer de la base de données sqlite et peut-être supprimer certains fichiers liés aux éléments de sdcard, supprimer simplement les éléments de la base de données et supprimer leurs fichiers associés et les ajouter à temp arraylist dansbackground thread
. puis dansUI thread
supprimer les éléments existants dans la liste des éléments temporaires de la liste des listes.J'espère que cela t'aides.
la source