O que eu quero fazer : execute um thread em segundo plano que calcule o conteúdo do ListView e atualize o ListView parcialmente, enquanto os resultados são calculados.
O que sei que tenho que evitar : não posso mexer com o conteúdo do ListAdapter do encadeamento em segundo plano, portanto, herdei o AsyncTask e publiquei o resultado (adicione entradas ao adaptador) do onProgressUpdate. Meu adaptador usa ArrayList dos objetos de resultado, todas as operações nessas listas de matriz são sincronizadas.
Pesquisa de outras pessoas : há dados muito valiosos aqui . Também sofri falhas quase diárias para um grupo de ~ 500 usuários e, quando adicionei o list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)
bloqueio no onProgressUpdate, falhas reduzidas em um fator de 10, mas não desapareceram. (foi sugerido em resposta )
O que recebi às vezes : observe que isso acontece muito raramente (uma vez por semana para um dos 3,5 mil usuários). Mas eu gostaria de me livrar desse bug completamente. Aqui está o stacktrace parcial:
`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)
[...]
Socorro? Não é mais necessário, veja abaixo
RESPOSTA FINAL: Como se viu, eu estava ligando a notifyDataSetChanged
cada 5 inserções para evitar oscilações e alterações repentinas da lista. Isso não pode ser feito dessa maneira, sempre notifique o adaptador quando a lista de base for alterada. Este bug há muito tempo para mim agora.
Respostas:
Eu tive o mesmo problema.
Eu estava adicionando itens ao meu
ArrayList
fora do thread da interface do usuário.Solução: fiz as duas coisas
adding the items
e chameinotifyDataSetChanged()
o thread da interface do usuário.fonte
Eu tive o mesmo problema, mas o corrigi usando o método
da classe
ListView
fonte
Este é um problema com vários segmentos e usando blocos sincronizados corretamente. Isso pode ser evitado. Sem colocar coisas extras no UI Thread e causar perda de capacidade de resposta do aplicativo.
Eu também enfrentei o mesmo. E, como a resposta mais aceita sugere que fazer alterações nos dados do adaptador do UI Thread pode resolver o problema. Isso funcionará, mas é uma solução rápida e fácil, mas não é a melhor.
Como você pode ver em um caso normal. A atualização do adaptador de dados do encadeamento em segundo plano e a chamada de notifyDataSetChanged no encadeamento da UI funcionam.
Essa ilegalStateException surge quando um thread da interface do usuário está atualizando a exibição e outro thread de segundo plano altera os dados novamente. Esse momento causa esse problema.
Portanto, se você sincronizará todo o código que está alterando os dados do adaptador e fazendo a chamada notifydatasetchange. Esse problema deve ter desaparecido. Como foi para mim e eu ainda estou atualizando os dados do segmento de plano de fundo.
Aqui está o código específico do meu caso para outras pessoas se referirem.
Meu carregador na tela principal carrega os contatos da lista telefônica em minhas fontes de dados em segundo plano.
Este PhoneBookManager.getPhoneBookContacts lê os contatos da agenda e os preenche nos hashmaps. Qual é diretamente utilizável para os Adaptadores de Lista desenharem a lista.
Há um botão na minha tela. Isso abre uma atividade em que esses números de telefone estão listados. Se eu definir diretamente oAdapter sobre a lista antes que o segmento anterior termine seu trabalho, isso significa que a navegação rápida ocorre com menos frequência. Aparece a exceção. Qual é o título desta pergunta SO. Então, eu tenho que fazer algo assim na segunda atividade.
Meu carregador na segunda atividade aguarda a conclusão do primeiro encadeamento. Até que ele mostre uma barra de progresso. Verifique o loadInBackground de ambos os carregadores.
Em seguida, ele cria o adaptador e o entrega para a atividade em que no thread da interface do usuário eu chamo setAdapter.
Isso resolveu meu problema.
Este código é apenas um trecho. Você precisa alterá-lo para compilar bem para você.
Espero que isto ajude
fonte
Eu resolvi isso por ter 2 listas. Uma lista eu uso apenas para o adaptador e faço todas as alterações / atualizações de dados na outra lista. Isso permite que eu faça atualizações em uma lista em um thread em segundo plano e atualize a lista "adapter" no thread principal / UI:
fonte
Eu escrevi esse código e ele foi executado em uma imagem de emulador 2.1 por ~ 12 horas e não recebeu a IllegalStateException. Vou dar ao framework android o benefício da dúvida sobre este e dizer que provavelmente é um erro no seu código. Eu espero que isso ajude. Talvez você possa adaptá-lo à sua lista e dados.
fonte
Alguns dias atrás, encontrei o mesmo problema e causou vários milhares de falhas por dia, cerca de 0,1% dos usuários atendem a essa situação. Eu tentei
setVisibility(GONE/VISIBLE)
erequestLayout()
, mas a contagem de acidentes diminui apenas um pouco.E finalmente resolvi. Nada com
setVisibility(GONE/VISIBLE)
. Nada comrequestLayout()
.Finalmente, descobri o motivo pelo qual usei um
Handler
para chamarnotifyDataSetChanged()
após atualizar os dados, o que pode levar a uma espécie de:checkForTap()
/onTouchEvent()
e finalmente chamalayoutChildren()
)notifyDataSetChanged()
e atualiza visualizaçõesE cometi outro erro que em
getCount()
,getItem()
egetView()
utilizo diretamente os campos no DataSource, em vez de copiá-los para o adaptador. Então, finalmente, ele falha quando:getCount()
egetView()
é chamado, e o listview descobre que os dados não são consistentes e lança exceções comojava.lang.IllegalStateException: The content of the adapter has changed but...
. Outra exceção comum éIndexOutOfBoundException
se você usar cabeçalho / rodapé noListView
.Portanto, a solução é fácil, basta copiar os dados para o adaptador do meu DataSource quando meu manipulador aciona o adaptador para obter dados e chamadas
notifyDataSetChanged()
. O acidente agora nunca acontece novamente.fonte
Se isso acontecesse de forma intermitente, acontece que eu só tive esse problema quando a lista foi rolada depois que um último item 'carregar mais' foi clicado. Se a lista não estivesse rolada, tudo funcionaria bem.
Após MUITA depuração, houve um erro da minha parte, mas também uma inconsistência no código do Android.
Quando a validação acontece, esse código é executado no ListView
Mas quando onChange acontece, ele dispara esse código no AdapterView (pai do ListView)
Observe como NÃO é garantido que o adaptador seja o mesmo!
No meu caso, como era um 'LoadMoreAdapter', eu estava retornando o WrappedAdapter na chamada getAdapter (para acessar os objetos subjacentes). Isso resultou em contagens diferentes devido ao item extra 'Carregar mais' e à exceção lançada.
Eu só fiz isso porque os documentos fazem parecer que está tudo bem em fazer
ListView.getAdapter javadoc
fonte
Meu problema estava relacionado ao uso de um filtro junto com o ListView.
Ao definir ou atualizar o modelo de dados subjacente do ListView, eu estava fazendo algo parecido com isto:
A chamada
filter()
na última linha fará (e deve) fazernotifyDataSetChanged()
com que seja chamada nopublishResults()
método do filtro . Às vezes, isso pode funcionar bem, especialmente no meu rápido Nexus 5. Mas, na realidade, está ocultando um bug que você notará em dispositivos mais lentos ou em condições de uso intensivo de recursos.O problema é que a filtragem é feita de forma assíncrona e, portanto, entre o final da
filter()
instrução e a chamada parapublishResults()
, ambos no encadeamento da interface do usuário, algum outro código de encadeamento da interface do usuário pode executar e alterar o conteúdo do adaptador.A correção real é fácil, basta ligar
notifyDataSetChanged()
também antes de solicitar a filtragem a ser realizada:fonte
Eu tenho uma lista se objetos de feed. É anexado e truncado a partir de um thread sem interface do usuário. Funciona bem com o adaptador abaixo. Eu chamo
FeedAdapter.notifyDataSetChanged
no thread da interface do usuário de qualquer maneira, mas um pouco mais tarde. Eu faço isso porque meus objetos Feed permanecem na memória no Serviço Local, mesmo quando a interface do usuário está inoperante.fonte
Estou enfrentando o mesmo problema com exatamente o mesmo log de erros. No meu caso,
onProgress()
o AsyncTask adiciona os valores ao adaptador usandomAdapter.add(newEntry)
. Para evitar que a interface do usuário se torne menos responsiva, definomAdapter.setNotifyOnChange(false)
e ligomAdapter.notifyDataSetChanged()
4 vezes no segundo. Uma vez por segundo, a matriz é classificada.Isso funciona bem e parece muito viciante, mas infelizmente é possível travá-lo tocando nos itens da lista exibidos com bastante frequência.
Mas parece que encontrei uma solução aceitável. Meu palpite é que, mesmo que você apenas trabalhe no thread da interface do usuário, o adaptador não aceita muitas alterações nos dados sem chamar
notifyDataSetChanged()
, por isso criei uma fila que está armazenando todos os novos itens até os 300ms mencionados. Se chegar esse momento, adiciono todos os itens armazenados de uma só vez e ligonotifyDataSetChanged()
. Até agora eu não conseguia mais travar a lista .fonte
Este é um bug conhecido no Android 4 a 4.4 (KitKat) e foi resolvido em "> 4.4"
Veja aqui: https://code.google.com/p/android/issues/detail?id=71936
fonte
Mesmo que eu tenha enfrentado o mesmo problema no meu aplicativo de notificação XMPP, a mensagem dos receptores precisa ser adicionada novamente à exibição de lista (implementada com
ArrayList
). Quando tentei adicionar o conteúdo do receptor atravésMessageListener
(thread separado), o aplicativo fecha com o erro acima. Resolvi isso adicionando o conteúdo ao meu métodoarraylist
&setListviewadapater
throughrunOnUiThread
, que faz parte da classe Activity. Isso resolveu meu problema.fonte
Enfrentei um problema semelhante, eis como resolvi no meu caso. Verifico se o
task
já existeRUNNING
ouFINISHED
porque uma tarefa pode ser executada apenas uma vez. Abaixo você verá um código parcial e adaptado da minha solução.fonte
Eu tive o mesmo problema e resolvi-o. Meu problema era que eu estava usando um
listview
, com um adaptador de matriz e com filtro. No método,performFiltering
eu estava mexendo com a matriz que possui os dados e era o problema, pois esse método não está sendo executado no thread da interface do usuário e, eventualmente, gera alguns problemas.fonte
Uma causa para esta falha é que o
ArrayList
objeto não pode mudar completamente. Portanto, quando removo um item, tenho que fazer o seguinte:Isso corrigiu o acidente para mim.
fonte
No meu caso, chamei o método
GetFilter()
em um adaptador a partir doTextWatcher()
método na Atividade principal e adicionei os dados com um loop For ativadoGetFilter()
. A solução foi alterar o loop For para oAfterTextChanged()
sub-método na Atividade principal e excluir a chamada paraGetFilter()
fonte
fonte
Eu também estava recebendo exatamente o mesmo erro e usando o AsyncTask:
Eu o resolvi colocando
adapter.notifyDataSetChanged();
na parte inferior do meu thread da interface do usuário, que é o meu método AsyncTask onPostExecute. Como isso :Agora meu aplicativo funciona.
EDIT: Na verdade, meu aplicativo ainda travava a cada 1 em 10 vezes, dando o mesmo erro.
Eventualmente, me deparei
runOnUiThread
com um post anterior, que achei que poderia ser útil. Então eu coloquei no meu método doInBackground, assim:E eu removi o
adapter.notifyDataSetChanged();
método. Agora, meu aplicativo nunca falha.fonte
Por favor, tente uma destas soluções:
Às vezes, se você adicionar um novo objeto à lista de dados em um thread (ou
doInBackground
método), esse erro ocorrerá. A solução é: crie uma lista temporária e inclua dados nessa lista no encadeamento (oudoInBackground
) e copie todos os dados da lista temporária para a lista de adaptadores no encadeamento da interface do usuário (ouonPostExcute
)Verifique se todas as atualizações da interface do usuário são chamadas no thread da interface do usuário.
fonte
eu tive o mesmo problema ao adicionar novos dados no carregador de imagens preguiçoso que acabei de colocar
no
espero que ajude você
fonte
Como o @Mullins disse "
eu adicionei os itens e liguei
notifyDataSetChanged()
no thread da interface do usuário e resolvi isso. - Mullins".No meu caso,
asynctask
eu chameinotifyDataSetChanged()
odoInBackground()
método e o problema foi resolvido. Quando ligueionPostExecute()
, recebi a exceção.fonte
Eu tinha um costume
ListAdapter
e estava ligandosuper.notifyDataSetChanged()
no início e não no final do métodofonte
Eu tinha a mesma situação, tinha muitos grupos de botões no meu item no listview e estava alterando alguns valores booleanos dentro do meu item, como holder.rbVar.setOnclik ...
meu problema ocorreu porque eu estava chamando um método dentro de getView (); e estava salvando um objeto dentro da preferência de compartilhamento, então eu tive o mesmo erro acima
Como eu resolvi isso; Eu removi meu método dentro de getView () para notifyDataSetInvalidated () e o problema desapareceu
fonte
Eu tive o mesmo problema. finalmente consegui a solução
antes de atualizar o listview, se o teclado virtual estiver presente, feche-o primeiro. depois disso, defina a fonte de dados e chame notifydatasetchanged ().
ao fechar o teclado internamente, o listview atualizará sua interface do usuário. ele continua ligando até fechar o teclado. nesse momento, se a fonte de dados mudar, ela lançará essa exceção. se os dados estiverem sendo atualizados no onActivityResult, há uma chance do mesmo erro.
fonte
Minha solução:
1) crie um
temp ArrayList
.2) faça seus trabalhos pesados (busca de linha do sqlite, ...) no
doInBackground
método e adicione itens à lista de matriz temporária.3) adicione todos os itens de temp araylist à sua lista de arrays do listview no
onPostExecute
métodonote:
convém excluir alguns itens do listview e também excluir do banco de dados sqlite e talvez excluir alguns arquivos relacionados aos itens do sdcard, apenas remover itens do banco de dados e remover seus arquivos relacionados e adicioná-los ao temp arraylist embackground thread
. então emUI thread
exclua os itens existentes na lista de matriz temporária da lista de matriz da exibição em lista.Espero que isto ajude.
fonte