AsyncTask
é ótimo executar tarefas complexas em outro thread.
Mas quando há uma mudança de orientação ou outra configuração enquanto ela AsyncTask
ainda está em execução, a corrente Activity
é destruída e reiniciada. E como a instância de AsyncTask
está conectada a essa atividade, ela falha e causa uma janela de mensagem "forçar fechamento".
Portanto, estou procurando algum tipo de "prática recomendada" para evitar esses erros e impedir que o AsyncTask falhe.
O que eu vi até agora é:
- Desative as alterações de orientação (com certeza não da maneira que você deve lidar com isso).
- Deixando a tarefa sobreviver e atualizando-a com a nova instância de atividade via
onRetainNonConfigurationInstance
- Apenas cancele a tarefa quando ela
Activity
for destruída e reinicie-a quando elaActivity
for criada novamente. - Vinculando a tarefa à classe do aplicativo em vez da instância da atividade.
- Algum método usado no projeto "prateleiras" (via onRestoreInstanceState)
Alguns exemplos de código:
Tarefas assíncronas do Android durante uma rotação de tela, parte I e parte II
Você pode me ajudar a encontrar a melhor abordagem que resolva melhor o problema e também seja fácil de implementar? O código em si também é importante, pois não sei como resolver isso corretamente.
Respostas:
Você NÃO utilizar
android:configChanges
para resolver este problema. Esta é uma prática muito ruim.Você não usar
Activity#onRetainNonConfigurationInstance()
qualquer um. Isso é menos modular e não é adequado paraFragment
aplicativos baseados em.Você pode ler meu artigo descrevendo como lidar com alterações de configuração usando
Fragment
s retidos . Ele resolve o problema de manter umaAsyncTask
alteração de rotação agradável. Basicamente, você precisa hospedar o seuAsyncTask
dentro deFragment
, chamarsetRetainInstance(true)
oFragment
e reportar oAsyncTask
progresso / resultados do mesmoActivity
através dos retidosFragment
.fonte
TabActivity
. Para ser sincero, não sei por que estamos falando sobre isso ... todos concordam que esseFragment
é o caminho a seguir. :)Normalmente, eu resolvo isso fazendo meu AsyncTasks acionar Intents de difusão no retorno de chamada .onPostExecute (), para que eles não modifiquem a Atividade que os iniciou diretamente. As Atividades ouvem essas transmissões com BroadcastReceivers dinâmicos e agem de acordo.
Dessa forma, as AsyncTasks não precisam se preocupar com a instância Activity específica que lida com o resultado. Eles apenas "gritam" quando terminam e, se uma Atividade estiver por esse período (ativa e focada / no estado retomado) que estiver interessada nos resultados da tarefa, ela será tratada.
Isso envolve um pouco mais de sobrecarga, já que o tempo de execução precisa lidar com a transmissão, mas geralmente não me importo. Eu acho que usar o LocalBroadcastManager em vez do sistema padrão em geral acelera um pouco as coisas.
fonte
Aqui está outro exemplo de AsyncTask que usa a
Fragment
para manipular alterações na configuração de tempo de execução (como quando o usuário gira a tela) comsetRetainInstance(true)
. Uma barra de progresso determinada (atualizada regularmente) também é demonstrada.O exemplo é parcialmente baseado nos documentos oficiais, Reter um objeto durante uma alteração na configuração .
Neste exemplo, o trabalho que requer um encadeamento em segundo plano é o mero carregamento de uma imagem da Internet na interface do usuário.
Alex Lockwood parece estar certo de que, quando se trata de lidar com alterações na configuração de tempo de execução com AsyncTasks, usar um "Fragmento Retido" é uma prática recomendada.
onRetainNonConfigurationInstance()
é preterido no Lint, no Android Studio. Os documentos oficiais nos alertam sobre o usoandroid:configChanges
, de Manipulação da mudança de configuração , ...Há a questão de saber se alguém deve usar um AsyncTask para o encadeamento em segundo plano.
A referência oficial do AsyncTask adverte ...
Como alternativa, pode-se usar um serviço, carregador (usando um CursorLoader ou AsyncTaskLoader) ou provedor de conteúdo para executar operações assíncronas.
Eu divido o resto do post em:
O procedimento
Comece com um AsyncTask básico como uma classe interna de uma atividade (ela não precisa ser uma classe interna, mas provavelmente será conveniente). Nesta fase, o AsyncTask não lida com alterações na configuração de tempo de execução.
Adicione uma classe aninhada RetainedFragment que estenda a classe Fragement e não tenha sua própria interface do usuário. Adicione setRetainInstance (true) ao evento onCreate deste fragmento. Forneça procedimentos para definir e obter seus dados.
No onCreate () da classe Activity mais externa, manipule RetainedFragment: referencie-o se ele já existir (no caso de a Atividade estar sendo reiniciada); crie e adicione-o se não existir; Em seguida, se ele já existir, obtenha dados do RetainedFragment e defina sua interface do usuário com esses dados.
Iniciar o AsyncTask a partir da interface do usuário
Adicione e codifique uma barra de progresso determinada:
Todo o código para o procedimento acima
Layout da atividade.
A atividade com: classe interna AsyncTask subclasse; classe interna RetainedFragment subclassed que lida com alterações na configuração de tempo de execução (por exemplo, quando o usuário gira a tela); e uma barra de progresso determinada que atualiza em intervalos regulares. ...
Neste exemplo, a função de biblioteca (mencionada acima com o prefixo explícito do pacote com.example.standardapplibrary.android.Network) que funciona de verdade ...
Adicione as permissões necessárias à sua tarefa em segundo plano ao AndroidManifest.xml ...
Adicione sua atividade ao AndroidManifest.xml ...
fonte
Recentemente, encontrei uma boa solução aqui . É baseado no salvamento de um objeto de tarefa via RetainConfiguration. Do meu ponto de vista, a solução é muito elegante e, para mim, comecei a usá-la. Você precisa apenas aninhar sua asynctask a partir do basetask e isso é tudo.
fonte
Com base nas respostas de @Alex Lockwood e nas respostas de @William & @quickdraw mcgraw nesta publicação: Como lidar com mensagens do manipulador quando a atividade / fragmento está em pausa , escrevi uma solução genérica.
Dessa forma, a rotação é manipulada e, se a atividade ficar em segundo plano durante a execução da tarefa assíncrona, a atividade receberá os retornos de chamada (onPreExecute, onProgressUpdate, onPostExecute & onCancelled) uma vez reiniciada, para que nenhuma IllegalStateException seja lançada (consulte Como lidar com o manipulador mensagens quando a atividade / fragmento está em pausa ).
Seria ótimo ter o mesmo, mas com tipos de argumentos genéricos, como um AsyncTask (por exemplo: AsyncTaskFragment <Params, Progress, Result>), mas não consegui fazê-lo rapidamente e não tenho tempo no momento. Se alguém quiser fazer a melhoria, sinta-se à vontade!
O código:
Você precisará do PauseHandler:
Uso da amostra:
fonte
Você pode usar carregadores para isso. Verifique Doc aqui
fonte
Para quem deseja evitar os fragmentos, é possível reter o AsyncTask executando alterações de orientação usando onRetainCustomNonConfigurationInstance () e alguma fiação.
(Observe que esse método é a alternativa ao onRetainNonConfigurationInstance () descontinuado ).
Parece que esta solução não é frequentemente mencionada. Eu escrevi um exemplo simples para ilustrar.
Felicidades!
fonte
Eu implementei uma biblioteca que pode resolver problemas com pausa e recreação de atividades enquanto sua tarefa está em execução.
Você deve implementar
AsmykPleaseWaitTask
eAsmykBasicPleaseWaitActivity
. Sua atividade e tarefa em segundo plano funcionarão bem, mesmo que você esteja, girará a tela e alternará entre aplicativosfonte
SOLUÇÃO RÁPIDA (não recomendado)
Para evitar que uma Atividade seja destruída e criada por si mesma, é necessário declarar sua atividade no arquivo de manifesto: android: configChanges = "guidance | keyboardHidden | screenSize
Como mencionado nos documentos
fonte