Nuestro control de calidad ha detectado un error: al girar el dispositivo Android (Droid Turbo), se produjo el siguiente bloqueo relacionado con RecyclerView :
java.lang.IndexOutOfBoundsException: Inconsistencia detectada. Posición de elemento no válida 2 (desplazamiento: 2). Estado: 3
Para mí, parece un error interno dentro de RecyclerView, ya que no puedo pensar en ninguna forma de que esto sea causado directamente por nuestro código ...
¿Alguien ha encontrado este problema?
¿Cuál sería la solución?
Una solución brutal podría ser quizás detectar la excepción cuando sucede y volver a crear la instancia de RecyclverView desde cero, para evitar quedarse con un estado corrupto.
Pero, si es posible, me gustaría entender mejor el problema (y quizás solucionarlo en su origen), en lugar de enmascararlo.
El error no es fácil de reproducir, pero es fatal cuando ocurre.
El seguimiento completo de la pila:
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
Respuestas:
Tuve un problema (posiblemente) relacionado: ingresar una nueva instancia de una actividad con un RecyclerView, pero con un adaptador más pequeño estaba desencadenando este bloqueo para mí.
RecyclerView.dispatchLayout()
puede intentar extraer elementos de la chatarra antes de llamarmRecycler.clearOldPositions()
. La consecuencia es que estaba extrayendo elementos del grupo común que tenían posiciones más altas que el tamaño del adaptador.Afortunadamente, solo hace esto si
PredictiveAnimations
está habilitado, por lo que mi solución fue subclasificarGridLayoutManager
(LinearLayoutManager
tiene el mismo problema y 'arreglar') y anularsupportsPredictiveItemAnimations()
para devolver falso:fuente
En mi caso (borrar / insertar datos en mi estructura de datos) ¡necesitaba borrar el grupo de reciclaje y luego notificar que el conjunto de datos cambió!
mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();
fuente
Use en su
notifyDataSetChanged()
lugarnotifyItem...
en este caso.fuente
notifyItem...
y corregirlo, que comenzará a funcionar en lugar de volver a representar todos los elementos.Resolví esto retrasando la
mRecycler.setAdapter(itemsAdapter)
caja registradora después de agregar todos los elementos al adaptadormRecycler.addAll(items)
y funcionó. No tengo idea de por qué lo hice para empezar, fue desde el código de una biblioteca que miré y vi esas líneas en el "orden incorrecto", estoy bastante seguro de que es así, por favor, si alguien puede confirmarlo, explique por qué ¿entonces? No estoy seguro si esta es una respuesta válida inclusofuente
swapAdapter(adapter, true)
lugar desetAdapter(adapter)
y ayudó.Tuve un problema similar pero no exactamente el mismo. En mi caso, en 1 punto estaba borrando la matriz que se pasó a la vista del reciclador
y no llamar a notifyDataSetChanged, ya que no quería que la vista del reciclador borrara inmediatamente las vistas. Estaba rellenando la matriz de mObjects en AsyncTask.
fuente
Tuve el mismo problema con recyclerView, así que acabo de notificar al adaptador sobre el cambio del conjunto de datos justo después de que se borre la lista.
fuente
Tengo el mismo problema. Ocurrió cuando me desplazaba rápido y llamaba a API y actualizaba los datos. Después de intentar todo para evitar un bloqueo, encontré la solución.
Funcionará.
fuente
Estoy alterando los datos para
RecyclerView
el fondoThread
. Tengo lo mismoException
que el OP. Agregué esto después de cambiar los datos:Espero eso ayude
fuente
view.recycler_view.post
, solíanotifyItemInserted
. En mi caso, ya ha sido hilo de la interfaz de usuario.Este error ocurre cuando la lista en el adaptador se borra cuando el usuario se desplaza, lo que hace que la posición del titular del elemento cambie, la referencia perdida entre la lista y el elemento en la interfaz de usuario, el error ocurra en la próxima solicitud "notifyDataSetChanged" .
Reparar:
Revise su método de lista de actualizaciones. Si haces algo como
Como arreglar. Cree un nuevo objeto de lista para el procesamiento del búfer y vuelva a asignarlo a la lista principal
Gracias a Nhan Cao por esta gran ayuda :)
fuente
Mi problema desapareció después de modificar mi
Adapter
implementación para usar una copia de la matriz de elementos en lugar de una referencia. SesetItems()
llama al método cada vez que tenemos nuevos elementos para mostrar en elRecyclerView
.En vez de:
Yo hice:
fuente
suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true);
y este es mi constructorpublic CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Me he enfrentado a la misma situación. Y se resolvió agregando códigos antes de borrar su colección.
mRecyclerView.getRecycledViewPool().clear();
fuente
En mi caso, estaba actualizando los elementos y llamando
notifyDataSetChanged
en un hilo no UI. La mayoría de las veces funcionó, pero cuando ocurrieron muchos cambios rápidamente, colapsó. Cuando lo hice, en cambio, básicamenteentonces dejó de estrellarse.
fuente
Solo necesita borrar su lista
OnPostExecute()
y no mientras lo hacePull to Refresh
Descubrí que esto sucede cuando te desplazas durante una extracción para actualizar , ya que estaba borrando la lista antes del
async task
, lo que resultó enjava.lang.IndexOutOfBoundsException: Inconsistency detected.
De esa manera no terminarás con una inconsistencia
fuente
También puede estar relacionado con la configuración del adaptador varias veces al mismo tiempo. Tenía un método de devolución de llamada que se activó 5-6 veces al mismo tiempo y estaba configurando el adaptador en esa devolución de llamada para que RecycledViewPool no pudiera manejar todos esos datos al mismo tiempo. Es una gran oportunidad, pero es mejor que lo revises de todos modos
fuente
Utilizar
en lugar
en este caso.
fuente
Para solucionar este problema, solo llame a notifyDataSetChanged () con una lista vacía antes de actualizar la vista de reciclaje.
Por ejemplo
hcpArray.clear (); // La lista para la vista de reciclaje de actualización
fuente
En mi caso, acabo de eliminar la línea con
setHasStableIds(true);
fuente
En mi caso, estaba tratando de cambiar el contenido de mi adaptador en un subproceso de fondo, pero llamé a notify * en el subproceso principal / ui.
¡Eso no es posible! La razón por la que forzar la notificación al hilo principal es que la vista del reciclador quiere que edite su adaptador de respaldo en el hilo principal, incluso en la misma pila de llamadas.
Para resolver el problema, asegúrese de que cada operación a su adaptador, así como cada notificación ... ¡la llamada se realice en la interfaz de usuario / hilo principal !
fuente
Me encontré con este desagradable rastro de pila con los nuevos componentes de arquitectura de Android recientemente. Esencialmente, tengo una lista de elementos en mi ViewModel que mi Fragmento observa, usando LiveData. Cuando ViewModel publica un nuevo valor para los datos, el Fragment actualiza el adaptador, pasa estos nuevos elementos de datos y notifica al adaptador que ha habido cambios.
Desafortunadamente, al pasar los nuevos elementos de datos al adaptador, no tuve en cuenta el hecho de que tanto el ViewModel como el Adaptador estarían apuntando a la misma referencia de objeto. Lo que significa que si actualizo los datos y llamo
postValue()
desde ViewModel, ¡hay una ventana muy pequeña donde los datos podrían actualizarse y el adaptador aún no ha sido notificado!Mi solución fue crear una instancia de una copia nueva de los elementos cuando se pasa al adaptador:
mList = new ArrayList<>(passedList);
Con esta solución súper fácil, puede estar seguro de que los datos de su adaptador no cambiarán hasta justo antes de que se notifique a su adaptador.
fuente
Esta es la única solución que funcionó para mí, incluso probando muchas de las soluciones anteriores.
1.) Intilización
2.) Escribe este método en el adaptador
stockListModels -> esta lista es la que está utilizando en el adaptador.
fuente
Para mí, funcionó después de agregar esta línea de código:
fuente
este problema puede ocurrir cuando intentas borrar tu lista, si vas a borrar tu lista de datos, especialmente cuando usas pull para actualizar, intenta usar un indicador booleano, inicialízalo como falso y dentro del método OnRefresh hazlo verdadero, borra tu lista de datos si el indicador es verdadero justo antes de agregarle los nuevos datos y luego hacerlo falso.
tu código podría ser así
fuente
Tuve un mismo problema anteriormente. Finalmente encontré una solución para eso
Lo que hago es notificar al adaptador que el elemento se ha eliminado y luego notificar que el rango del conjunto de datos del adaptador ha cambiado
fuente
Me encontré con un problema similar y lo descubrí. Codifiqué algunos ejemplos para un caso de prueba, pero no me aseguré de que cada uno devolviera una identificación única y eso me causó el siguiente bloqueo. La solución de los ID resolvió el problema, ¡espero que esto ayude a alguien más!
fuente
Una vez recibí el error también:
Causa: estaba tratando de actualizar una vista de reciclador desde una tarea asíncrona mientras intentaba simultáneamente obtener antiguos viewHolders borrados;
Código: Genero datos con solo presionar un botón, lógica de la siguiente manera
Problema: cada vez que me desplazo rápido antes de generar mis datos, obtengo
Solución: en lugar de borrar el RecyclerView antes de generar mis datos, lo dejo y luego lo Reemplace con los Nuevos Datos, el Call NotifyDatasetChanged, como se muestra a continuación;
fuente
Simplemente elimine todas las vistas de su Administrador de diseño antes de notificar. me gusta:
fuente
Usar
ListAdapter (androidx.recyclerview.widget.ListAdapter)
llamadaadapter.submitList(null)
antes de llamaradapter.submitList(list)
:fuente
Esta excepción se produjo en API 19, 21 (pero no nueva). En la rutina de Kotlin, cargué datos (en subproceso de fondo) y en el subproceso de interfaz de usuario agregué y les mostré:
Adaptador:
Por alguna razón, Android no se procesa lo suficientemente rápido u otra cosa, por lo tanto, actualizo una lista en el
post
método deRecyclerView
(agregar, eliminar, actualizar eventos de elementos):Esta excepción es similar a "No se puede llamar a este método en una devolución de llamada de desplazamiento. Las devoluciones de llamada de desplazamiento se pueden ejecutar durante un paso de medida y diseño en el que no se pueden cambiar los datos de RecyclerView. Cualquier llamada de método que pueda cambiar la estructura de RecyclerView o el contenido del adaptador se debe posponer para siguiente fotograma ": Recyclerview: no se puede llamar a este método en una devolución de llamada de desplazamiento .
fuente
Encontré esa configuración mRecycler.setLayoutFrozen (verdadero); en el método onRefresh del swipeContainer.
Resuelto el problema para mí.
fuente
Este es un error bastante desagradable.
Para manejar el clic de mi artículo, utilicé una implementación
RecyclerView.OnItemTouchListener
similar a la solución encontrada en esta pregunta .Después de muchas veces de actualizar la
RecyclerView
fuente de datos y hacer clic en un elemento, estoIndexOutOfBoundsException
podría bloquear mi aplicación. Cuando se hace clic en un elemento,RecyclerView
internamente busca la vista subyacente correcta y le devuelve la posición. Al revisar el código fuente, vi que había algunosTasks
y losThreads
programé. Para abreviar la historia, básicamente es solo un estado ilegal donde dos fuentes de datos están entremezcladas y no sincronizadas y todo se vuelve loco.En base a esto, eliminé mi implementación de
RecyclerView.OnItemTouchListener
y simplemente capturé el clic en elViewHolder
deAdapter
mí mismo:Puede que esta no sea la mejor solución, pero está libre de bloqueos por ahora. Espero que esto le ahorre algo de tiempo :).
fuente