Nosso controle de qualidade detectou um erro: ao girar o dispositivo Android (Droid Turbo), ocorreu o seguinte acidente relacionado ao RecyclerView :
java.lang.IndexOutOfBoundsException: Inconsistência detectada. Posição 2 do item inválida (deslocamento: 2) .state: 3
Para mim, parece um erro interno no RecyclerView, pois não consigo pensar em nenhuma maneira de isso ser causado diretamente pelo nosso código ...
Alguém já encontrou este problema?
Qual seria a solução?
Uma solução brutal pode ser a captura da exceção quando isso acontece e recriar a instância do RecyclverView do zero, para evitar ficar com um estado corrompido.
Mas, se possível, eu gostaria de entender melhor o problema (e talvez corrigi-lo na fonte), em vez de mascará-lo.
O bug não é fácil de reproduzir, mas é fatal quando acontece.
O rastreamento de pilha completo:
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
Respostas:
Eu tive um problema (possivelmente) relacionado - inserir uma nova instância de uma atividade com um RecyclerView, mas com um adaptador menor, estava causando esse travamento para mim.
RecyclerView.dispatchLayout()
pode tentar retirar itens do refugo antes de ligarmRecycler.clearOldPositions()
. A conseqüência é que ele estava puxando itens do pool comum que tinham posições maiores que o tamanho do adaptador.Felizmente, ele só faz isso se
PredictiveAnimations
estiver ativado, então minha solução foi subclassarGridLayoutManager
(LinearLayoutManager
tem o mesmo problema e 'consertar') e substituirsupportsPredictiveItemAnimations()
para retornar false:fonte
No meu caso (excluir / inserir dados na minha estrutura de dados), eu precisava limpar o pool de reciclagem e notificar o conjunto de dados alterado!
mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();
fonte
Use em
notifyDataSetChanged()
vez dissonotifyItem...
neste caso.fonte
notifyItem...
e a correção, que começará a funcionar em vez de renderizar novamente todos os itens.Resolvi isso atrasando o
mRecycler.setAdapter(itemsAdapter)
caixa depois de adicionar todos os itens ao adaptadormRecycler.addAll(items)
e ele funcionou. Não faço ideia por que fiz isso, foi a partir do código de uma biblioteca que olhei e vi essas linhas na "ordem errada". Tenho certeza de que é isso, por favor, se alguém puder confirmar, explique por que é tão? Não tenho certeza se esta é uma resposta válida, mesmofonte
swapAdapter(adapter, true)
vez desetAdapter(adapter)
e ajudou.Eu tive um problema semelhante, mas não exatamente o mesmo. No meu caso, em um ponto, eu estava limpando a matriz que foi passada para a revisão de reciclagem
e não chamando notifyDataSetChanged, pois eu não queria que a recyclerview limpe imediatamente as visualizações. Eu estava preenchendo novamente a matriz mObjects no AsyncTask.
fonte
Eu tive o mesmo problema com o recyclerView. Acabei de notificar o adaptador sobre a alteração do conjunto de dados logo após a limpeza da lista.
fonte
Eu tenho o mesmo problema. Ocorreu quando eu estava rolando rapidamente e chamando a API e atualizando dados. Depois de tentar todas as coisas para evitar falhas, encontrei a solução.
Vai funcionar.
fonte
Estou alterando os dados para o
RecyclerView
segundo planoThread
. Eu tenho o mesmoException
que o OP. Eu adicionei isso depois de alterar os dados:Espero que ajude
fonte
view.recycler_view.post
, eu useinotifyItemInserted
. No meu caso, já foi thread da interface do usuário.Este erro ocorre quando a lista no adaptador é limpa quando a rolagem do usuário faz com que a posição do suporte do item seja alterada, refs perdida entre a lista e o item na interface do usuário, o erro ocorra no próximo "notifyDataSetChanged" solicitação .
Consertar:
Revise seu método da lista de atualizações. Se você fizer algo como
Como consertar. Crie um novo objeto de lista para processamento de buffer e atribua novamente à lista principal depois disso.
Obrigado a Nhan Cao por esta grande ajuda :)
fonte
Meu problema desapareceu depois que modifiquei minha
Adapter
implementação para usar uma cópia da matriz de itens em vez de uma referência. OsetItems()
método é chamado cada vez que temos novos itens para mostrar noRecyclerView
.Ao invés de:
Eu fiz:
fonte
suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true);
e este é meu construtorpublic CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Eu já enfrentei a mesma situação. E foi resolvido adicionando códigos antes de limpar sua coleção.
mRecyclerView.getRecycledViewPool().clear();
fonte
No meu caso, eu estava atualizando os itens e chamando
notifyDataSetChanged
um thread que não era da interface do usuário. Na maioria das vezes funcionava, mas quando muitas mudanças aconteciam rapidamente, elas travavam. Quando eu fiz, basicamenteentão parou de bater.
fonte
Você só precisa limpar sua lista
OnPostExecute()
e não enquanto estiverPull to Refresh
Descobri que isso acontece quando você rola durante um pull para atualizar , pois eu estava limpando a lista antes de
async task
, resultando emjava.lang.IndexOutOfBoundsException: Inconsistency detected.
Dessa forma, você não terminará com uma inconsistência
fonte
Também pode estar relacionado à configuração do adaptador várias vezes ao mesmo tempo. Eu tinha um método de retorno de chamada que foi acionado 5 a 6 vezes ao mesmo tempo e estava configurando o adaptador nesse retorno de chamada para que o RecycledViewPool não pudesse lidar com todos esses dados de maneira contemporânea. É uma grande chance, mas é melhor você conferir de qualquer maneira.
fonte
Usar
em vez de
nesse caso.
fonte
Para corrigir esse problema, chame notifyDataSetChanged () com lista vazia antes de atualizar a exibição de reciclagem.
Por exemplo
hcpArray.clear (); // A lista para exibição de reciclagem de atualização
fonte
No meu caso, acabei de remover a linha com
setHasStableIds(true);
fonte
No meu caso, eu estava tentando alterar o conteúdo do meu adaptador em um thread em segundo plano, mas chamei notify * no thread principal / interface do usuário.
Isso não é possível! O motivo pelo qual a notificação é forçada ao encadeamento principal é que a reciclagem deseja que você edite seu adaptador de suporte no encadeamento principal, mesmo na mesma pilha de chamadas.
Para resolver o problema, verifique se todas as operações no adaptador e todas as notificações ... são feitas no thread da interface do usuário / principal !
fonte
Encontrei esse rastreio de pilha desagradável com os novos componentes da arquitetura do Android recentemente. Essencialmente, tenho uma lista de itens no meu ViewModel que são observados pelo meu fragmento, usando o LiveData. Quando o ViewModel publica um novo valor para os dados, o Fragmento atualiza o adaptador, passando esses novos elementos de dados e notificando o adaptador de que houve alterações.
Infelizmente, ao passar os novos elementos de dados para o adaptador, não considerei o fato de que o ViewModel e o Adapter estivessem apontando para a mesma referência de objeto! Ou seja, se eu atualizar os dados e ligar
postValue()
de dentro do ViewModel, há uma janela muito pequena onde os dados podem ser atualizados e o adaptador ainda não foi notificado!Minha correção foi instanciar uma cópia nova dos elementos quando passados para o adaptador:
mList = new ArrayList<>(passedList);
Com essa correção super fácil, você pode garantir que os dados do adaptador não serão alterados até pouco antes de o adaptador ser notificado.
fonte
Esta é a única solução que funcionou para mim, mesmo tentando muitas das soluções acima.
1.) fertilização
2.) Escreva este método no adaptador
stockListModels -> esta lista é a que você está usando no adaptador.
fonte
Para mim, funcionou depois de adicionar esta linha de código:
fonte
esse problema pode ocorrer quando você tenta limpar sua lista, se você estiver limpando sua lista de dados, especialmente quando estiver usando pull para atualizar, tente usar um sinalizador booleano, inicialize-o como false e, se o método OnRefresh for verdadeiro, limpe seu dataList se o flag for verdadeiro imediatamente antes de adicionar os novos dados a ele e depois torná-lo falso.
seu código pode ser assim
fonte
Eu tive um mesmo problema anteriormente. Finalmente encontrei uma solução alternativa para isso
O que faço é notificar o adaptador que o item foi removido e notificar o intervalo do conjunto de dados do adaptador alterado
fonte
Corri para um problema semelhante e só descobri. Codifiquei alguns exemplos para um caso de teste, mas não garanti que cada um retornasse um ID exclusivo e que causasse a falha abaixo para mim. A correção dos IDs resolveu o problema. Espero que isso ajude outra pessoa!
fonte
uma vez eu também recebi o erro:
Causa: Eu estava tentando atualizar uma Exibição do Reciclador de uma tarefa assíncrona enquanto tentava obter antigos viewHolders excluídos;
Código: eu gero dados com o pressionar de um botão, lógica da seguinte maneira
Problema: sempre que eu rolar rapidamente antes de gerar meus dados, recebo
Solução: em vez de limpar o RecyclerView antes de gerar meus dados, eu o deixo e o substituo pelos Novos dados, a chamada NotifyDatasetChanged, como mostrado abaixo;
fonte
Basta remover todas as visualizações do seu Gerenciador de layout antes de notificar. gostar:
fonte
Usando a
ListAdapter (androidx.recyclerview.widget.ListAdapter)
chamadaadapter.submitList(null)
antes de ligaradapter.submitList(list)
:fonte
Essa exceção foi gerada na API 19, 21 (mas não nova). Na corotina Kotlin, carreguei dados (no thread de segundo plano) e no thread da interface do usuário adicionei e mostrei a eles:
Adaptador:
Por algum motivo, o Android não é renderizado rápido o suficiente ou qualquer outra coisa, então eu atualizo uma lista no
post
método doRecyclerView
(adicionar, remover, atualizar eventos dos itens):Essa exceção é semelhante a "Não é possível chamar esse método em um retorno de chamada de rolagem. Os retornos de chamada de rolagem podem ser executados durante uma passagem de medida e layout em que você não pode alterar os dados do RecyclerView. Qualquer chamada de método que possa alterar a estrutura do RecyclerView ou o conteúdo do adaptador deve ser adiada próximo quadro. ": Reciclerview - não é possível chamar esse método em um retorno de chamada de rolagem .
fonte
Eu achei que a configuração mRecycler.setLayoutFrozen (true); no método onRefresh do swipeContainer.
resolveu o problema para mim.
fonte
Este é um bug bastante desagradável.
Para lidar com o clique do item, usei uma implementação
RecyclerView.OnItemTouchListener
semelhante à solução encontrada nesta pergunta .Depois de muitas vezes atualizando a
RecyclerView
fonte de dados e clicando em um item, issoIndexOutOfBoundsException
travaria meu aplicativo. Quando um item é clicado, oRecyclerView
interna procura a visão subjacente correta e devolve sua posição. Verificando o código fonte, vi que havia algunsTasks
eThreads
agendados. Para resumir a história, basicamente é apenas um estado ilegal em que duas fontes de dados são misturadas e não sincronizadas e a coisa toda fica louca.Com base nisso, eu removi minha implementação do
RecyclerView.OnItemTouchListener
e simplesmente pegou o clique sobre oViewHolder
doAdapter
mim mesmo:Esta pode não ser a melhor solução, mas está livre de falhas por enquanto. Espero que isso poupe algum tempo :).
fonte