Eu tenho investigado esse problema há meses, encontrei soluções diferentes para ele, das quais não estou feliz, pois todos são hacks maciços. Ainda não consigo acreditar que uma classe que falhou no design entrou na estrutura e ninguém está falando sobre isso, então acho que devo estar perdendo alguma coisa.
O problema está com AsyncTask
. De acordo com a documentação
"permite executar operações em segundo plano e publicar resultados no thread da interface do usuário sem ter que manipular threads e / ou manipuladores."
O exemplo continua a mostrar como showDialog()
é chamado um método exemplar onPostExecute()
. Isso, no entanto, parece inteiramente artificial para mim, porque mostrar uma caixa de diálogo sempre precisa de uma referência a um válido Context
, e um AsyncTask nunca deve ter uma referência forte a um objeto de contexto .
O motivo é óbvio: e se a atividade for destruída, o que desencadeou a tarefa? Isso pode acontecer o tempo todo, por exemplo, porque você virou a tela. Se a tarefa contiver uma referência ao contexto que a criou, você não estará apenas segurando um objeto de contexto inútil (a janela será destruída e qualquer interação da interface do usuário falhará com uma exceção!), Você corre o risco de criar um vazamento de memória.
A menos que minha lógica seja falha aqui, isso se traduz em: onPostExecute()
é totalmente inútil, porque de que serve esse método para executar no thread da interface do usuário se você não tiver acesso a nenhum contexto? Você não pode fazer nada significativo aqui.
Uma solução alternativa seria não passar instâncias de contexto para um AsyncTask, mas uma Handler
instância. Isso funciona: como um manipulador vincula livremente o contexto e a tarefa, você pode trocar mensagens entre eles sem arriscar um vazamento (certo?). Mas isso significaria que a premissa do AsyncTask, a saber, que você não precisa se preocupar com os manipuladores, está errada. Também parece abusar do Handler, pois você está enviando e recebendo mensagens no mesmo thread (você o cria no thread da interface do usuário e o envia em onPostExecute (), que também é executado no thread da interface do usuário).
Além disso, mesmo com essa solução alternativa, você ainda tem o problema de que, quando o contexto é destruído, você não tem registro das tarefas que ele disparou. Isso significa que você deve reiniciar qualquer tarefa ao recriar o contexto, por exemplo, após uma alteração na orientação da tela. Isso é lento e desperdício.
Minha solução para isso (conforme implementada na biblioteca Droid-Fu ) é manter um mapeamento de WeakReference
s dos nomes dos componentes para suas instâncias atuais no objeto de aplicativo exclusivo. Sempre que um AsyncTask é iniciado, ele registra o contexto de chamada nesse mapa e, em cada retorno de chamada, ele busca a instância de contexto atual desse mapeamento. Isso garante que você nunca faça referência a uma instância de contexto obsoleta e sempre tenha acesso a um contexto válido nos retornos de chamada, para que você possa fazer um trabalho significativo na interface do usuário. Também não vaza, porque as referências são fracas e são limpas quando não existe mais instância de um determinado componente.
Ainda assim, é uma solução alternativa complexa e requer a subclasse de algumas das classes da biblioteca Droid-Fu, tornando essa uma abordagem bastante intrusiva.
Agora, eu simplesmente quero saber: Estou apenas perdendo alguma coisa ou o AsyncTask é realmente totalmente defeituoso? Como suas experiências estão trabalhando com isso? Como você resolveu esse problema?
Obrigado pela sua contribuição.