Quando chamar o contexto da atividade OU o contexto do aplicativo?

265

Tem havido muitas postagens sobre o que esses dois contextos são. Mas ainda não entendi direito

Pelo que entendi até agora: Cada um é uma instância de sua classe, o que significa que alguns programadores recomendam o uso o this.getApplicationContext()mais rápido possível para não "vazar" nenhuma memória. Isso ocorre porque o outro this(obtendo o Activitycontexto da instância) aponta para um Activityque está sendo destruído toda vez que o usuário inclina o telefone ou sai do aplicativo etc. O que aparentemente o Garbage Collector (GC) não captura e, portanto, usa muita memória ..

Mas alguém pode, por favor, apresentar alguns exemplos de codificação realmente bons, onde seria a coisa certa a ser usada this(obtendo o contexto da Activityinstância atual ) e o contexto do aplicativo será inútil / errado?

Norfeldt
fonte

Respostas:

408

getApplicationContext()quase sempre está errado. Hackborn (entre outros) tem sido muito explícita que você usa getApplicationContext()quando sabe por que está usando getApplicationContext()e somente quando precisa usá-lo getApplicationContext().

Para ser franco, "alguns programadores" usam getApplicationContext()(ou getBaseContext(), em menor grau) porque sua experiência em Java é limitada. Eles implementam uma classe interna (por exemplo, um OnClickListenerpara um Buttonem um Activity) e precisam de um Context. Em vez de usar MyActivity.thispara chegar à classe externa ' this, eles usam getApplicationContext()ou getBaseContext()para obter um Contextobjeto.

Você usa getApplicationContext()quando sabe que precisa Contextde algo que pode durar mais tempo do que qualquer outro que possa Contextter à sua disposição. Os cenários incluem:

  • Use getApplicationContext()se você precisar de algo vinculado a um Contextque tenha escopo global. Eu uso getApplicationContext(), por exemplo, em WakefulIntentService, para a estática WakeLocka ser usada para o serviço. Desde que WakeLocké estático, e eu preciso de um Contextpara chegar a PowerManagercriá-lo, é mais seguro de usar getApplicationContext().

  • Use getApplicationContext()quando você ligar a um Servicede um Activity, se desejar passar o ServiceConnection(ou seja, o identificador para o vínculo) entre Activityinstâncias via onRetainNonConfigurationInstance(). O Android rastreia internamente as ligações por meio delas ServiceConnectionse mantém referências àquelas Contextsque criam as ligações. Se você ligar a partir de Activity, a nova Activityinstância terá uma referência à ServiceConnectionque possui uma referência implícita à antiga Activity, e a antiga Activitynão poderá ser coletada como lixo.

Alguns desenvolvedores usam subclasses personalizadas Applicationpara seus próprios dados globais, dos quais eles recuperam getApplicationContext(). Isso é certamente possível. Prefiro membros de dados estáticos, se por nenhum outro motivo, você puder ter apenas umApplication objeto personalizado . Criei um aplicativo usando um Applicationobjeto personalizado e achei doloroso. Hackborn também concorda com esta posição .

Aqui estão as razões pelas quais não usar getApplicationContext()onde quer que você vá:

  • Não é um completo Context, suportando tudo o que Activityfaz. Várias coisas que você tentará fazer com isso Contextfalharão, principalmente relacionadas à GUI .

  • Ele pode criar vazamentos de memória, se o Contextfrom se getApplicationContext()apegar a algo criado por suas chamadas que você não limpa. Com um Activity, se ele se agarra a alguma coisa, uma vez que o Activitylixo é coletado, todo o resto também sai. O Applicationobjeto permanece durante toda a vida útil do seu processo.

CommonsWare
fonte
1
Muito obrigado por esta resposta. Outro link que encontrei antes de ler esta resposta também pode ajudar algumas pessoas. stackoverflow.com/questions/7298731/… - este link explica minhas preocupações sobre o vazamento de memória.
Norfeldt 5/09/11
27
@Norfeldt: FYI, o link no seu comentário está de volta a esta resposta.
CommonsWare
2
obrigado .. este foi o link: stackoverflow.com/questions/5796611/... descreve a fuga de memória que eu estava com medo de ficar usando este
Norfeldt
6
@djaqeel: A última parte da sua cotação é quase verdadeira. É melhor formulado como "não dê um contexto de Atividade a algo que viverá mais do que a Atividade, como um membro de dados estático". No entanto, você ainda o usa apenas getApplicationContext()quando sabe exatamente por que precisa dele em uma determinada situação. Inflar um layout? Use a atividade. Ligação a um serviço, onde você precisa dessa ligação para sobreviver a uma alteração na configuração? Use getApplicationContext(), para que a ligação não esteja vinculada à Activityinstância.
CommonsWare
7
@Ever: eu cubro isso na minha resposta. Dave Smith também tem uma excelente postagem no blog que aborda os contextos: doubleencore.com/2013/06/context Seu parágrafo de resumo: "Na maioria dos casos, use o Contexto diretamente disponível para você no componente anexo no qual você está trabalhando. Você pode seguramente segurar uma referência a ela, desde que essa referência não se estenda além do ciclo de vida desse componente.Quando você precisar salvar uma referência a um Contexto de um objeto que vive além de sua Atividade ou Serviço, mesmo que temporariamente, alterne a referência salva até o contexto do aplicativo ".
CommonsWare
48

Acho que há muitas coisas mal documentadas no site do SDK, essa é uma delas. A afirmação que vou fazer é que parece melhor usar como padrão um contexto de aplicativo e usar apenas um contexto de atividade quando você realmente precisar. O único lugar em que eu já vi que você precisa de um contexto de atividade é para uma caixa de diálogo de progresso. SBERG412 alega que você precisa usar um contexto de atividade para uma mensagem de brinde, mas os documentos do Android mostram claramente um contexto de aplicativo que está sendo usado. Eu sempre usei o contexto do aplicativo para brindes por causa deste exemplo do Google. Se estiver errado, o Google largou a bola aqui.

Aqui está mais para pensar e analisar:

Para uma mensagem de brinde, o Guia do desenvolvedor do Google usa o contexto do aplicativo e diz explicitamente para usá-lo: Notificações de brinde

Na seção de diálogos do guia Dev, você vê que um AlertDialog.Builder usa o contexto do aplicativo e, em seguida, a barra de progresso usa um contexto de atividade. Isso não é explicado pelo Google. Diálogos

Parece que um bom motivo para usar o contexto do aplicativo é quando você deseja lidar com alterações de configuração como uma mudança de orientação e deseja manter objetos que precisam de um contexto como Views. Se você olhar aqui: Alterações no tempo de execução Há um cuidado ao usar um contexto de atividade, que pode criar um vazamento. Isso pode ser evitado com um contexto de aplicativo com as visualizações que devem ser mantidas (pelo menos, esse é o meu entendimento). Em um aplicativo que estou escrevendo, pretendo usar um contexto de aplicativo porque estou tentando adiar algumas visualizações e outras coisas em uma mudança de orientação, e ainda quero que a atividade seja destruída e recriada em alterações de orientação. Portanto, tenho que usar um contexto de aplicativo para não causar vazamento de memória (consulte Evitando vazamentos de memória) Para mim, parece haver muitas boas razões para usar o contexto do aplicativo em vez de um contexto de atividade, e para mim parece que você usaria com mais frequência do que um contexto de atividade. É o que muitos livros do Android que eu já passei parecem fazer, e é o que muitos dos exemplos do Google que eu já vi fazer.

A documentação do Google realmente parece que o uso do contexto do aplicativo é perfeitamente adequado na maioria dos casos e, na verdade, aparece com mais frequência do que o uso do contexto de atividade em seus exemplos (pelo menos nos exemplos que eu já vi). Se é realmente um problema usar o contexto do aplicativo, o Google realmente precisa colocar mais ênfase nisso. Eles precisam deixar claro e refazer alguns de seus exemplos. Eu não culparia isso inteiramente por desenvolvedores inexperientes, já que a autoridade (Google) realmente parece que não é um problema usar contextos de aplicativos.

Andi Jay
fonte
5
Eu concordo completamente. A resposta do CommonsWare me surpreendeu um pouco. Estou feliz por ter encontrado essa pergunta, porque na documentação do Google sugere que o uso de getApplicationContext pode ser tão perigoso.
Steve Schwarcz
38

Usei esta tabela como uma orientação para quando usar os diferentes tipos de contexto, como o contexto de aplicativo (ou seja:) getApplicationContext()e o contexto de atividade , também o contexto BroadcastReceiver :

insira a descrição da imagem aqui

Todos os méritos vão para o autor original aqui para mais informações.

CommonSenseCode
fonte
11

Qual contexto usar?

Existem dois tipos de contexto:

  1. O contexto do aplicativo está associado ao aplicativo e sempre será o mesmo durante toda a vida útil do aplicativo - ele não muda. Portanto, se você estiver usando o Toast, poderá usar o contexto do aplicativo ou mesmo o contexto da atividade (ambos) porque o toast pode ser exibido de qualquer lugar do aplicativo e não está anexado a uma janela específica. Mas há muitas exceções, uma delas é quando você precisa usar ou passar no contexto da atividade.

  2. O contexto da atividade está associado à atividade e pode ser destruído se a atividade for destruída - pode haver várias atividades (mais do que provável) com um único aplicativo. E às vezes você absolutamente precisa do identificador do contexto da atividade. Por exemplo, se você iniciar uma nova atividade, precisará usar o contexto da atividade em sua Intenção, para que a nova atividade de inicialização seja conectada à atividade atual em termos de pilha de atividades. No entanto, você também pode usar o contexto do aplicativo para iniciar uma nova atividade, mas precisa definir o sinalizador Intent.FLAG_ACTIVITY_NEW_TASKcom a intenção de tratá-la como uma nova tarefa.

Vamos considerar alguns casos:

  • MainActivity.this refere-se ao contexto MainActivity que estende a classe Activity, mas a classe base (atividade) também estende a classe Context, para que possa ser usada para oferecer contexto de atividade.

  • getBaseContext() oferece contexto de atividade.

  • getApplication() oferece contexto de aplicativo.

  • getApplicationContext() também oferece contexto de aplicativo.

Para mais informações, consulte este link .

Zohra Khan
fonte
E o caso em que é necessário exibir um AlertDialog no aplicativo, por exemplo, um processo assíncrono mostrando um resultado. Um exemplo disso pode ser : o usuário clica no download, isso aciona uma solicitação de download downloadmanagere, quando o sinal finalizado é recebido, ele deve exibir uma caixa de diálogo, como "O que você deseja fazer com este download?". Minha solução (hack) salva a mais recente Activityde uma static Applicationclasse e solicita a atual Activityquando o download estiver concluído. No entanto, duvido que esta seja a implementação adequada. TL; DR Como exibir o AlertDialog em qualquer lugar do aplicativo?
CybeX
@KGCybeX Se você quiser exibir qualquer coisa e em qualquer lugar do seu aplicativo quando o download terminar, registre manualmente um receptor de transmissão em sua atividade que escute uma mensagem específica que seu serviço de download transmitirá e faça o que quiser ao receber a mensagem ou anexe sua atividade para esse serviço diretamente.
ExiRouS 18/06
6

Fiquei me perguntando por que não usar o Contexto do Aplicativo para todas as operações que ele suporta. No final, reduz a chance de vazamento de memória e falta de verificação nula para getContext () ou getActivity () (ao usar o contexto do aplicativo injetado ou adquirido através do método estático do aplicativo). Declarações, como a da Sra. Hackborn para usar o Contexto do Aplicativo apenas se necessário, não me parecem convincentes sem uma explicação. Mas parece que eu descobri um porquê:

descobriram que há problemas em algumas combinações de versão / dispositivo Android que não seguem essas regras. Por exemplo, se eu tenho um BroadcastReceiver ao qual é transmitido um Contexto e o converto em Contexto de Aplicativo e, em seguida, tento chamar registerReceiver () no Contexto de Aplicativo, há muitas instâncias em que isso funciona bem, mas também muitas instâncias em que recebo uma falha devido a uma ReceiverCallNotAllowedException. Essas falhas ocorrem em uma ampla variedade de versões do Android, da API 15 até 22. https://possiblemobile.com/2013/06//text /# comment- 2443283153

Como não é garantido que todas as operações descritas como suportadas pelo Contexto do aplicativo na tabela abaixo funcionem em todos os dispositivos Android! insira a descrição da imagem aqui

Malachiasz
fonte
4

Dois ótimos exemplos de quando você deve usar o contexto de atividade versus o contexto de aplicativo são quando exibir uma mensagem Toast ou uma mensagem de diálogo interna, pois o uso do contexto de aplicativo causará uma exceção:

ProgressDialog.show(this, ....);

ou

Toast t = Toast.makeText(this,....);

Ambos precisam de informações do contexto de Atividade que não são fornecidas no contexto de Aplicativo.

SBerg413
fonte
5
Hum .. Qual versão do sistema operacional Android você testou? Eu testei no 4.4.4 e funciona bem. Além disso, como @Andi Jay mencionou, o documento oficial do desenvolvedor do Android usou o contexto do aplicativo em seu código de amostra. developer.android.com/guide/topics/ui/notifiers/…
#
1
@ Nome chinês, sim, pode funcionar, mas em algum momento no futuro desse aplicativo, ele também falhará. Aconteceu comigo várias vezes.
Ojonugwa Jude Ochalifu
1
Quando uso o contexto de atividade no Toast, ele vaza memória!
Jemshit Iskenderov 5/08
3

Contexto de aplicação viver até que a sua aplicação está vivo somente e não é depender de Atividade do Ciclo de Vida, mas, contexto objeto keep longa vida . Se o objeto usado temporariamente, esse tempo usar o Contexto do Aplicativo e o Contexto da Atividade será totalmente oposto ao Contexto do Aplicativo.

Ganesh Katikar
fonte