Quand appeler le contexte d'activité OU le contexte d'application?

265

Il y a eu beaucoup de messages sur ce que sont ces deux contextes .. Mais je ne comprends toujours pas bien

Si je comprends bien jusqu'à présent: Chacun est une instance de sa classe, ce qui signifie que certains programmeurs vous recommandent d'utiliser le this.getApplicationContext()plus souvent possible afin de ne pas "fuir" la mémoire. En effet, l'autre this(obtention du Activitycontexte d'instance) pointe vers un élément Activityqui est détruit à chaque fois que l'utilisateur incline le téléphone ou quitte l'application, etc. ..

Mais quelqu'un peut-il s'il vous plaît trouver de très bons exemples de codage où ce serait la bonne chose à utiliser this(obtenir le contexte de l' Activityinstance actuelle ) et le contexte de l'application sera inutile / faux?

Norfeldt
la source

Réponses:

408

getApplicationContext()est presque toujours faux. Mme Hackborn (entre autres) ont été très explicites que vous n'utilisez quand vous savez pourquoi vous utilisez et que lorsque vous avez besoin d'utiliser .getApplicationContext()getApplicationContext()getApplicationContext()

Pour être franc, «certains programmeurs» utilisent getApplicationContext()(ou getBaseContext(), dans une moindre mesure) parce que leur expérience Java est limitée. Ils implémentent une classe interne (par exemple, un OnClickListenerpour un Buttondans un Activity) et ont besoin d'un Context. Plutôt que d'utiliser MyActivity.thispour accéder à la classe externe ' this, ils utilisent getApplicationContext()ou getBaseContext()pour obtenir un Contextobjet.

Vous n'utilisez quegetApplicationContext() lorsque vous savez que vous avez besoin d'un Contextpour quelque chose qui peut vivre plus longtemps que tout autre que Contextvous avez probablement à votre disposition. Les scénarios comprennent:

  • À utiliser getApplicationContext()si vous avez besoin de quelque chose lié à un Contextqui aura une portée globale. J'utilise getApplicationContext(), par exemple, dans WakefulIntentService, pour la statique WakeLockà utiliser pour le service. Étant donné que WakeLockc'est statique, et j'ai besoin d'un Contextpour arriver à PowerManagerle créer, il est plus sûr à utiliser getApplicationContext().

  • À utiliser getApplicationContext()lorsque vous vous liez à un à Servicepartir d'un Activity, si vous souhaitez passer le ServiceConnection(c'est-à-dire le handle à la liaison) entre les Activityinstances via onRetainNonConfigurationInstance(). Android suit en interne les liaisons via celles ServiceConnections- ci et contient des références à celles Contextsqui créent les liaisons. Si vous effectuez une liaison à partir de Activity, la nouvelle Activityinstance aura une référence à la ServiceConnectionqui a une référence implicite à l'ancienne Activity, et l'ancienne Activityne peut pas être récupérée.

Certains développeurs utilisent des sous-classes personnalisées Applicationpour leurs propres données globales, qu'ils récupèrent via getApplicationContext(). C'est certainement possible. Je préfère les membres de données statiques, si pour aucune autre raison que vous ne pouvez avoir qu'un seulApplication objet personnalisé . J'ai créé une application en utilisant un Applicationobjet personnalisé et je l'ai trouvé douloureux. Mme Hackborn est également d'accord avec cette position .

Voici les raisons pour lesquelles ne pas utiliser getApplicationContext()partout où vous allez:

  • Ce n'est pas un Contextsupport complet , supportant tout ce qui le Activityfait. Diverses choses que vous essaierez de faire avec cela Contextéchoueront, principalement liées à l'interface graphique .

  • Cela peut créer des fuites de mémoire, si le Contextfrom getApplicationContext()tient quelque chose créé par vos appels que vous ne nettoyez pas. Avec un Activity, s'il tient à quelque chose, une fois que les Activityordures sont récupérées, tout le reste déborde aussi. L' Applicationobjet reste pour la durée de vie de votre processus.

CommonsWare
la source
1
Merci beaucoup pour cette réponse. Un autre lien que j'ai trouvé avant de lire cette réponse pourrait également aider certaines personnes. stackoverflow.com/questions/7298731/… - ce lien explique mes préoccupations concernant les fuites de mémoire.
Norfeldt
27
@Norfeldt: FYI, le lien dans votre commentaire renvoie à cette réponse.
CommonsWare
2
merci .. c'était le lien: stackoverflow.com/questions/5796611/… il décrit la fuite de mémoire que j'avais peur d'obtenir en utilisant ceci
Norfeldt
6
@djaqeel: La dernière partie de votre citation est presque vraie. Il est mieux formulé comme «ne donnez pas de contexte d'activité à quelque chose qui vivra plus longtemps que l'activité, comme un membre de données statique». Cependant, vous ne l'utilisez toujours que getApplicationContext()lorsque vous savez précisément pourquoi vous en avez besoin dans une situation donnée. Gonfler une mise en page? Utilisez l'activité. Liaison à un service, où avez-vous besoin de cette liaison pour survivre à un changement de configuration? Utilisez getApplicationContext(), de sorte que la liaison n'est pas liée à l' Activityinstance.
CommonsWare
7
@Sever: Je couvre cela dans ma réponse. Dave Smith a également un excellent article de blog couvrant les contextes: doubleencore.com/2013/06/context Son paragraphe récapitulatif: "Dans la plupart des cas, utilisez le contexte directement disponible à partir du composant englobant dans lequel vous travaillez. Vous pouvez une référence à celui-ci tant que cette référence ne s'étend pas au-delà du cycle de vie de ce composant. Dès que vous devez enregistrer une référence à un contexte à partir d'un objet qui vit au-delà de votre activité ou service, même temporairement, changez cette référence que vous enregistrez sur le contexte de l'application. "
CommonsWare
48

Je pense qu'il y a beaucoup de choses qui sont mal documentées sur le site du SDK, c'est l'une d'entre elles. L'affirmation que je vais faire est qu'il semble qu'il vaut mieux utiliser par défaut un contexte d'application et n'utiliser un contexte d'activité que lorsque vous en avez vraiment besoin. Le seul endroit où j'ai jamais vu que vous avez besoin d'un contexte d'activité est pour une boîte de dialogue de progression. SBERG412 prétend que vous devez utiliser un contexte d'activité pour un message de toast, mais les documents Android montrent clairement un contexte d'application utilisé. J'ai toujours utilisé le contexte d'application pour les toasts à cause de cet exemple de Google. Si c'est mal de le faire, Google a laissé tomber la balle ici.

Voici plus à penser et à examiner:

Pour un message toast, le guide Google Dev utilise le contexte de l'application et dit explicitement de l'utiliser: Toast Notifications

Dans la section des boîtes de dialogue du guide de développement, vous voyez qu'un AlertDialog.Builder utilise le contexte de l'application, puis la barre de progression utilise un contexte d'activité. Cela n'est pas expliqué par Google. Dialogues

Il semble qu'une bonne raison d'utiliser le contexte d'application soit lorsque vous souhaitez gérer des changements de configuration comme un changement d'orientation et que vous souhaitez conserver les objets qui nécessitent un contexte tel que des vues. Si vous regardez ici: Modifications de l'heure d'exécution Il y a une mise en garde concernant l'utilisation d'un contexte d'activité, qui peut créer une fuite. Cela peut être évité avec un contexte d'application avec les vues qui doivent être conservées (du moins c'est ce que je comprends). Dans une application que j'écris, j'ai l'intention d'utiliser un contexte d'application parce que j'essaie de conserver certaines vues et d'autres choses sur un changement d'orientation, et je veux toujours que l'activité soit détruite et recréée sur les changements d'orientation. Je dois donc utiliser un contexte d'application pour ne pas provoquer de fuite de mémoire (voir Éviter les fuites de mémoire). Il me semble qu'il existe de nombreuses bonnes raisons d'utiliser le contexte d'application au lieu d'un contexte d'activité, et il me semble presque que vous l'utiliseriez plus souvent qu'un contexte d'activité. C'est ce que de nombreux livres Android que j'ai consultés semblent faire, et c'est ce que font la plupart des exemples Google que j'ai vus.

La documentation de Google donne vraiment l'impression que l'utilisation du contexte d'application est parfaitement correcte dans la plupart des cas, et apparaît en fait plus souvent que l'utilisation d'un contexte d'activité dans leurs exemples (du moins les exemples que j'ai vus). Si c'est vraiment un tel problème d'utiliser le contexte d'application, alors Google doit vraiment mettre davantage l'accent sur cela. Ils doivent être clairs et ils doivent refaire certains de leurs exemples. Je ne blâmerais pas cela entièrement pour les développeurs inexpérimentés car l'autorité (Google) donne vraiment l'impression que ce n'est pas un problème d'utiliser des contextes d'application.

Andi Jay
la source
5
Je suis complètement d'accord. La réponse de CommonsWare m'a surpris un peu. Je suis content d'avoir trouvé cette question, car la documentation de Google suggère que l'utilisation de getApplicationContext peut être si dangereuse.
Steve Schwarcz
38

J'ai utilisé ce tableau comme guide pour savoir quand utiliser les différents types de contexte tels que le contexte d'application (c'est-à-dire:) getApplicationContext()et le contexte d'activité , également le contexte BroadcastReceiver :

entrez la description de l'image ici

Tous les mérites vont à l'auteur original ici pour plus d'informations.

CommonSenseCode
la source
11

Quel contexte utiliser?

Il existe deux types de contexte:

  1. Le contexte d'application est associé à l'application et sera toujours le même pendant toute la durée de vie de l'application - il ne change pas. Donc, si vous utilisez Toast, vous pouvez utiliser le contexte d'application ou même le contexte d'activité (les deux) car le toast peut être affiché de n'importe où dans votre application et n'est pas attaché à une fenêtre spécifique. Mais il existe de nombreuses exceptions, une exception lorsque vous devez utiliser ou transmettre le contexte d'activité.

  2. Le contexte d'activité est associé à l'activité et peut être détruit si l'activité est détruite - il peut y avoir plusieurs activités (plus que probablement) avec une seule application. Et parfois, vous avez absolument besoin de la poignée de contexte d'activité. Par exemple, si vous lancez une nouvelle activité, vous devez utiliser le contexte d'activité dans son intention afin que la nouvelle activité de lancement soit connectée à l'activité actuelle en termes de pile d'activités. Cependant, vous pouvez également utiliser le contexte de l'application pour lancer une nouvelle activité, mais vous devez ensuite définir l'indicateur Intent.FLAG_ACTIVITY_NEW_TASKdans l'intention de la traiter comme une nouvelle tâche.

Prenons quelques cas:

  • MainActivity.this fait référence au contexte MainActivity qui étend la classe Activity mais la classe de base (activité) étend également la classe Context, de sorte qu'elle peut être utilisée pour offrir un contexte d'activité.

  • getBaseContext() offre un contexte d'activité.

  • getApplication() offre un contexte d'application.

  • getApplicationContext() offre également un contexte d'application.

Pour plus d'informations, veuillez consulter ce lien .

Zohra Khan
la source
Qu'en est-il du cas où l'on a besoin d'afficher un AlertDialog dans l'application, par exemple un processus asynchrone montrant un résultat. Un exemple de ceci peut être : l'utilisateur clique sur le téléchargement, cela déclenche une demande de téléchargement downloadmanageret, lorsque le signal terminé est reçu, il devrait afficher une boîte de dialogue, par exemple "Que voulez-vous faire avec ce téléchargement?". Ma solution (hack) enregistre le plus récent Activityd'une static Applicationclasse et demande le courant Activitylorsque le téléchargement est terminé. Cependant, je doute que ce soit la bonne mise en œuvre. TL; DR Comment afficher AlertDialog n'importe où dans l'application?
CybeX
@KGCybeX Si vous souhaitez afficher n'importe quoi et n'importe où dans votre application à la fin du téléchargement, vous devez enregistrer manuellement un récepteur de diffusion sur votre activité qui écoute un message spécifique que votre service de téléchargement diffusera et faire ce que vous voulez à la réception du message, ou joindre votre activité directement à ce service.
ExiRouS
6

Je me demandais pourquoi ne pas utiliser le contexte d'application pour chaque opération qu'il prend en charge. En fin de compte, cela réduit le risque de fuite de mémoire et de vérification nulle manquante pour getContext () ou getActivity () (lors de l'utilisation d'un contexte d'application injecté ou acquis via une méthode statique à partir d'Application). Des déclarations, comme celle de Mme Hackborn pour utiliser le contexte d'application uniquement si nécessaire, ne me semblent pas convaincantes sans expliquer pourquoi. Mais il semble que j'ai trouvé un non-juré pourquoi:

ont constaté qu'il existe des problèmes sur certaines combinaisons de version / appareil Android qui ne respectent pas ces règles. Par exemple, si j'ai un BroadcastReceiver auquel est passé un contexte et que je convertis ce contexte en contexte d'application et que j'essaie ensuite d'appeler registerReceiver () sur le contexte d'application, il y a de nombreuses instances où cela fonctionne correctement, mais aussi de nombreuses instances où j'obtiens un plantage en raison d'une exception ReceiverCallNotAllowedException. Ces plantages se produisent sur une large gamme de versions Android de l'API 15 à 22. https://possiblemobile.com/2013/06/context/#comment-2443283153

Parce qu'il n'est pas garanti que toutes les opérations décrites comme prises en charge par le contexte d'application dans le tableau ci-dessous fonctionneront sur tous les appareils Android! entrez la description de l'image ici

Malachiasz
la source
4

Deux grands exemples de cas où vous devez utiliser le contexte d'activité par rapport au contexte d'application sont lorsque l'affichage d'un message Toast ou d'un message de dialogue intégré car l'utilisation du contexte d'application provoquera une exception:

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

ou

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

Ces deux éléments nécessitent des informations du contexte d'activité qui ne sont pas fournies dans le contexte d'application.

SBerg413
la source
5
Hm .. Quelle version du système d'exploitation Android avez-vous testée? J'ai testé sur 4.4.4 et cela fonctionne bien. De plus, comme @Andi Jay l'a mentionné, le document officiel du développeur Android a utilisé le contexte de l'application dans son exemple de code. developer.android.com/guide/topics/ui/notifiers/...
김준호
1
@Chinese name, yes it might work but but some in the future of this app, it will also crash. Happened to plusieurs fois.
Ojonugwa Jude Ochalifu
1
Lorsque j'utilise le contexte d'activité dans Toast, cela fuit de la mémoire!
Jemshit Iskenderov
3

Contexte d'application en direct jusqu'à ce que votre demande est en vie seulement et il ne dépend de l'activité du cycle de vie , mais, le contexte objet donjon longue durée . Si l'objet qui vous est utilisé temporairement, cette fois-ci, utilisez le contexte d'application et le contexte d'activité est totalement opposé au contexte d'application.

Ganesh Katikar
la source