Notre QA a détecté un bug: lors de la rotation de l'appareil Android (Droid Turbo), le crash lié à RecyclerView suivant s'est produit:
java.lang.IndexOutOfBoundsException: incohérence détectée. Position de l'élément non valide 2 (décalage: 2). État: 3
Pour moi, cela ressemble à une erreur interne à l'intérieur de RecyclerView, car je ne pense pas que cela puisse être causé directement par notre code ...
est-ce que quelqu'un à rencontré ce problème?
Quelle serait la solution?
Une solution de contournement brutale pourrait être de rattraper l'exception lorsqu'elle se produit et de recréer l'instance RecyclverView à partir de zéro, pour éviter de se retrouver avec un état corrompu.
Mais, si possible, j'aimerais mieux comprendre le problème (et peut-être le résoudre à sa source), au lieu de le masquer.
Le bogue n'est pas facile à reproduire, mais il est fatal lorsqu'il se produit.
La trace de pile complète:
W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40)
E/AndroidRuntime( 7546): FATAL EXCEPTION: main
E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546
E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382)
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523)
E/AndroidRuntime( 7546): at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179)
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942)
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237)
E/AndroidRuntime( 7546): at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132)
E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)
E/AndroidRuntime( 7546): at andro
Réponses:
J'ai eu un problème (peut-être) lié - la saisie d'une nouvelle instance d'une activité avec RecyclerView, mais avec un adaptateur plus petit déclenchait ce crash pour moi.
RecyclerView.dispatchLayout()
peut essayer de retirer des éléments de la ferraille avant d'appelermRecycler.clearOldPositions()
. La conséquence étant qu'il tirait des éléments du pool commun qui avaient des positions supérieures à la taille de l'adaptateur.Heureusement, il ne fait cela que si
PredictiveAnimations
sont activés, donc ma solution était de sous-classerGridLayoutManager
(LinearLayoutManager
a le même problème et «corriger»), et de remplacersupportsPredictiveItemAnimations()
pour retourner false:la source
Dans mon cas (supprimer / insérer des données dans ma structure de données), j'avais besoin d'effacer le pool de recyclage, puis de notifier l'ensemble de données modifié!
mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();
la source
Utilisez
notifyDataSetChanged()
plutôtnotifyItem...
dans ce cas.la source
notifyItem...
et la correction qui commenceront à fonctionner au lieu de restituer tous les éléments.J'ai résolu cela en retardant la
mRecycler.setAdapter(itemsAdapter)
caisse après avoir ajouté tous les éléments à l'adaptateur avecmRecycler.addAll(items)
et cela a fonctionné. Je ne sais pas pourquoi j'ai fait ça pour commencer, c'est à partir du code d'une bibliothèque que j'ai regardé et vu ces lignes dans le "mauvais ordre", je suis à peu près sûr que c'est bien cela, s'il vous plaît si quelqu'un peut le confirmer expliquer pourquoi c'est alors? Je ne sais pas si c'est une réponse valable mêmela source
swapAdapter(adapter, true)
au lieu desetAdapter(adapter)
et cela a aidé.J'ai eu un problème similaire mais pas exactement le même. Dans mon cas, à un moment donné, je nettoyais le tableau qui a été transmis à la vue d'ensemble
et ne pas appeler notifyDataSetChanged, car je ne voulais pas que la vue de recyclage efface immédiatement les vues. Je remplissais de nouveau le tableau mObjects dans AsyncTask.
la source
J'ai eu le même problème avec recyclerView. Je viens donc d'informer l'adaptateur du changement de jeu de données juste après l'effacement de la liste.
la source
J'ai le même problème. Cela s'est produit lorsque je faisais défiler rapidement et appelais l'API et mettais à jour les données.Après avoir tout essayé pour éviter un crash, j'ai trouvé une solution.
Ça va marcher.
la source
Je modifie les données de
RecyclerView
l'arrière-planThread
. J'ai obtenu la même choseException
que l'OP. J'ai ajouté ceci après avoir changé les données:J'espère que ça aide
la source
view.recycler_view.post
, j'ai utilisénotifyItemInserted
. Dans mon cas, il s'agit déjà d'un thread d'interface utilisateur.Cette erreur se produit lorsque la liste dans l'adaptateur s'efface lors du défilement de l'utilisateur qui modifie la position du support d'élément, la référence perdue entre la liste et l'élément sur l'interface utilisateur, une erreur se produit dans le prochain "notifyDataSetChanged" .
Réparer:
Passez en revue votre méthode de liste de mise à jour. Si vous faites quelque chose comme
Comment réparer. Créez un nouvel objet de liste pour le traitement du tampon et attribuez-le à nouveau à la liste principale après cela
Merci à Nhan Cao pour cette grande aide :)
la source
Mon problème a disparu après avoir modifié mon
Adapter
implémentation pour utiliser une copie du tableau des éléments au lieu d'une référence. LasetItems()
méthode est appelée chaque fois que nous avons de nouveaux éléments à afficher dans leRecyclerView
.Au lieu de:
J'ai fait:
la source
suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true);
et c'est mon constructeurpublic CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
J'ai fait face à la même situation. Et cela a été résolu en ajoutant des codes avant d'effacer votre collection.
mRecyclerView.getRecycledViewPool().clear();
la source
Dans mon cas, je mettais à jour les éléments et appelais
notifyDataSetChanged
un thread non UI. La plupart du temps, cela fonctionnait, mais lorsque de nombreux changements se produisaient rapidement, cela plantait. Quand je l'ai fait, à la place, essentiellementpuis il a cessé de s'écraser.
la source
Il vous suffit de vider votre liste
OnPostExecute()
et non en faisantPull to Refresh
J'ai découvert que cela se produit lorsque vous faites défiler pendant une traction pour actualiser , car j'effaçais la liste avant le
async task
, ce qui entraînejava.lang.IndexOutOfBoundsException: Inconsistency detected.
De cette façon, vous ne terminerez pas avec une incohérence
la source
Il peut également être lié à la configuration de l'adaptateur plusieurs fois en même temps. J'avais une méthode de rappel qui a été déclenchée 5-6 fois en même temps et je configurais l'adaptateur dans ce rappel afin que RecycledViewPool ne puisse pas gérer toutes ces données simultanément. C'est une grosse chance mais vous feriez mieux de le vérifier de toute façon.
la source
Utilisation
au lieu
dans ce cas.
la source
Pour résoudre ce problème, appelez simplement notifyDataSetChanged () avec une liste vide avant de mettre à jour la vue de recyclage.
Par exemple
hcpArray.clear (); // La liste pour la vue de recyclage des mises à jour
la source
Dans mon cas, je viens de supprimer la ligne avec
setHasStableIds(true);
la source
Dans mon cas, j'essayais de modifier le contenu de mon adaptateur sur un thread d'arrière-plan, mais j'ai appelé notify * sur le thread principal / ui.
Ce n'est pas possible! La raison pour laquelle notifier est forcée sur le thread principal est que la vue de recyclage veut que vous modifiez votre adaptateur de sauvegarde sur le thread principal, même sur la même pile d'appels.
Pour résoudre le problème, assurez-vous que chaque opération sur votre adaptateur ainsi que chaque notification ... est effectuée sur le thread ui / main !
la source
J'ai rencontré cette méchante trace de pile avec les nouveaux composants d'architecture Android récemment. Essentiellement, j'ai une liste d'éléments dans mon ViewModel qui sont observés par mon fragment, en utilisant LiveData. Lorsque le ViewModel publie une nouvelle valeur pour les données, le fragment met à jour l'adaptateur, transmettant ces nouveaux éléments de données et notifiant à l'adaptateur qu'il y a eu des modifications.
Malheureusement, lors de la transmission des nouveaux éléments de données à l'adaptateur, je n'ai pas tenu compte du fait que le ViewModel et l'adaptateur pointeraient vers la même référence d'objet! Cela signifie que si je mets à jour les données et appelle
postValue()
depuis le ViewModel, il y a une très petite fenêtre où les données pourraient être mises à jour et l'adaptateur pas encore notifié!Mon correctif consistait à instancier une nouvelle copie des éléments une fois transmis à l'adaptateur:
mList = new ArrayList<>(passedList);
Avec cette solution super simple, vous pouvez être assuré que les données de votre adaptateur ne changeront pas juste avant que votre adaptateur ne soit notifié.
la source
Ce n'est que la solution qui a fonctionné pour moi, même en essayant de nombreuses solutions ci-dessus.
1.) Intilization
2.) Écrivez cette méthode dans l'adaptateur
stockListModels -> cette liste est celle que vous utilisez dans l'adaptateur.
la source
Pour moi, cela a fonctionné après avoir ajouté cette ligne de code:
la source
ce problème peut se produire lorsque vous essayez d'effacer votre liste, si vous voulez effacer votre liste de données, en particulier lorsque vous utilisez pull pour actualiser, essayez d'utiliser un indicateur booléen, initialisez-le comme faux et à l'intérieur de la méthode OnRefresh rendez-le vrai, effacez votre dataList si l'indicateur est vrai juste avant d'y ajouter les nouvelles données et ensuite de le rendre faux.
votre code pourrait être comme ça
la source
J'ai eu un même problème auparavant. Enfin trouvé une solution de contournement pour cela
Ce que je fais est d'aviser l'adaptateur que l'élément a été supprimé, puis de notifier la plage de jeu de données de l'adaptateur modifiée
la source
J'ai rencontré un problème similaire et je l'ai découvert. J'ai codé en dur quelques exemples pour un cas de test, mais je ne me suis pas assuré qu'ils retournaient chacun un ID unique et cela a provoqué le crash ci-dessous pour moi. La correction des ID a résolu le problème, j'espère que cela aidera quelqu'un d'autre!
la source
j'ai aussi eu une fois l'erreur:
Cause: j'essayais de mettre à jour une vue Recycler à partir d'une tâche asynchrone tout en essayant d'obtenir simultanément les anciens viewHolders supprimés;
Code: je génère des données en appuyant sur un bouton, logique comme suit
Problème: chaque fois que je défile rapidement avant de générer mes données, je reçois
Solution: au lieu d'effacer le RecyclerView avant de générer mes données, je le laisse à la place, puis je le remplace par les nouvelles données, l'appel NotifyDatasetChanged, comme indiqué ci-dessous;
la source
Supprimez simplement toutes les vues de votre gestionnaire de mise en page avant de le notifier. comme:
la source
Utiliser l'
ListAdapter (androidx.recyclerview.widget.ListAdapter)
appeladapter.submitList(null)
avant d'appeleradapter.submitList(list)
:la source
Cette exception levée sur API 19, 21 (mais pas nouvelle). Dans Kotlin coroutine, j'ai chargé des données (dans le fil d'arrière-plan) et dans le fil d'interface utilisateur ajouté et leur ai montré:
Adaptateur:
Pour une raison quelconque, Android ne s'affiche pas assez rapidement ou autre chose, donc, je mets à jour une liste dans la
post
méthode deRecyclerView
(ajouter, supprimer, mettre à jour les événements des éléments):Cette exception est similaire à "Impossible d'appeler cette méthode dans un rappel de défilement. Les rappels de défilement peuvent être exécutés lors d'une passe de mesure et de mise en page où vous ne pouvez pas modifier les données RecyclerView. Tout appel de méthode susceptible de modifier la structure de RecyclerView ou le contenu de l'adaptateur doit être reporté sur le image suivante. ": Recyclerview - ne peut pas appeler cette méthode dans un rappel de défilement .
la source
J'ai trouvé que la définition de mRecycler.setLayoutFrozen (true); dans la méthode onRefresh du swipeContainer.
résolu le problème pour moi.
la source
C'est un bug assez désagréable.
Pour gérer le clic de mon article, j'ai utilisé une implémentation
RecyclerView.OnItemTouchListener
similaire à la solution trouvée dans cette question .Après plusieurs fois d'actualisation de la
RecyclerView
source de données de et en cliquant sur un élément, celaIndexOutOfBoundsException
ferait planter mon application. Lorsqu'un élément est cliqué, leRecyclerView
logiciel recherche en interne la vue sous-jacente correcte et lui redonne sa position. En vérifiant le code source, j'ai vu qu'il y en avaitTasks
etThreads
prévu. Pour résumer l'histoire, il s'agit simplement d'un état illégal où deux sources de données sont mélangées et non synchronisées et le tout se déchaîne.Sur cette base, j'ai supprimé mon implémentation du
RecyclerView.OnItemTouchListener
et j'ai simplement pris le clic surViewHolder
leAdapter
moi - même:Ce n'est peut-être pas la meilleure solution, mais une solution sans crash pour le moment .. J'espère que cela vous fera gagner du temps :).
la source