getApplication () vs getApplicationContext ()

417

Je n'ai pas pu trouver de réponse satisfaisante à cela, alors allons-y: quel est le problème Activity/Service.getApplication()et Context.getApplicationContext()?

Dans notre application, les deux retournent le même objet. Dans un cas ActivityTestCasecependant, se moquer de l'application fera getApplication()revenir avec la maquette, mais getApplicationContextretournera toujours une instance de contexte différente (une injectée par Android). Est-ce un bug? Est-ce exprès?

Je ne comprends même pas la différence en premier lieu. Existe-t-il des cas en dehors d'une suite de tests où les deux appels peuvent revenir avec des objets différents? Quand et pourquoi? De plus, pourquoi est getApplicationdéfini sur Activityet Service, mais pas sur Context? Ne devrait-il pas toujours y avoir une instance d'application valide disponible de n'importe où ?

Matthias
la source
8
Bonne question. Les tests sont un peu mystérieux (comme vous le savez bien). Mais je me demande si une différence se manifeste dans ces deux appels de méthode si vous ne créez pas explicitement un Applicationobjet dans votre application.
Christopher Orr

Réponses:

366

Question très intéressante. Je pense que c'est principalement un sens sémantique, et peut également être dû à des raisons historiques.

Bien que dans les implémentations actuelles d'Activité et de Service Android, getApplication()et getApplicationContext()renvoyant le même objet, il n'y a aucune garantie que ce sera toujours le cas (par exemple, dans une implémentation de fournisseur spécifique).

Donc, si vous voulez la classe Application que vous avez enregistrée dans le manifeste, vous ne devez jamais l' appeler getApplicationContext()et la transtyper dans votre application, car il ne s'agit peut-être pas de l'instance d'application (que vous avez manifestement expérimentée avec le framework de test).

Pourquoi getApplicationContext()existe- t- il en premier lieu?

getApplication()est uniquement disponible dans la classe Activity et la classe Service, alors qu'il getApplicationContext()est déclaré dans la classe Context.

Cela signifie en fait une chose: lorsque vous écrivez du code dans un récepteur de diffusion, qui n'est pas un contexte mais reçoit un contexte dans sa méthode onReceive, vous ne pouvez qu'appeler getApplicationContext(). Ce qui signifie également que vous n'êtes pas assuré d'avoir accès à votre application dans un BroadcastReceiver.

Lorsque vous regardez le code Android, vous voyez que lorsqu'elle est attachée, une activité reçoit un contexte de base et une application, et ce sont des paramètres différents. getApplicationContext()délègue son appel baseContext.getApplicationContext().

Une dernière chose: la documentation indique que dans la plupart des cas, vous ne devriez pas avoir besoin de sous-classer Application:

Il n'y a normalement pas besoin de sous-classe Application. Dans la plupart des situations, les singletons statiques peuvent fournir les mêmes fonctionnalités de manière plus modulaire. Si votre singleton a besoin d'un contexte global (par exemple pour enregistrer des récepteurs de diffusion), la fonction pour le récupérer peut être affectée à un Contextqui l'utilise en interne Context.getApplicationContext()lors de la première construction du singleton.

Je sais que ce n'est pas une réponse exacte et précise, mais est-ce que cela répond à votre question?

Pierre-Yves Ricau
la source
89
@ Piwaï: N'écoutez pas le doc. Le sous android.app.Application- classement est super complet. Par exemple, j'ai eu des problèmes sans fin lors de l'initialisation de la base de données. Une fois emménagé, Application.onCreateil fonctionnait comme un charme. Maintenant, je fais toute l'initialisation à l'échelle du système Applicationet je n'écrirais pas une autre application sans.
Martin
9
@Martin Le fait de ne pas écouter les documents signifie généralement que votre code peut se casser à l'avenir, ou même maintenant dans des conditions inattendues, perdre sa portabilité, mal fonctionner, empêcher les développeurs de la plate-forme de faire un changement bénéfique (ce qui brise l'hypothèse que vous avez incorrectement faite bien qu'elle ait été basé uniquement sur l'implémentation actuelle, pas sur les documents). Je pense que c'est un très mauvais comportement et un très mauvais conseil.
Palec
17
@Palec: "Il n'est normalement pas nécessaire de sous-classer l'application." - C'est juste un indice. J'utilise toujours des fonctionnalités officiellement documentées de la manière prévue. - J'avais l'habitude d'utiliser ces «singletons statiques» au début et ils se sont avérés être une douleur dans le a… - L'initialisation paresseuse a ses problèmes. Surtout lorsqu'il est utilisé avec des tests d'instrumentation. - J'ai encore ces singletons pour la modularité mais je les instancie en bloc dans le onCreate d'une sous-classe android.app.Application. - fonctionne comme un charme.
Martin
9
@Martin J'aurais dû être clair: ma réaction ne concernait que la première phrase. "N'écoute pas le doc." Il s'agit généralement d'un conseil très dangereux. Mais "C'est juste un indice - vous pouvez ignorer le doc dans ce cas si vous avez une raison et je vais vous en montrer une ..." me semble absolument OK.
Palec
3
"lorsque vous écrivez du code dans un récepteur de diffusion, qui n'est pas un contexte mais reçoit un contexte dans sa méthode onReceive, vous ne pouvez appeler que getApplicationContext (). Ce qui signifie également que vous n'êtes PAS garanti d'avoir accès à votre application dans un BroadcastReceiver. " Alors, que pouvons-nous faire pour accéder à ma classe d'application dans BroadcastReceiver?
Dr.jacky
30

Comparez getApplication()et getApplicationContext().

getApplicationrenvoie un Applicationobjet qui vous permettra de gérer l'état de votre application globale et de répondre à certaines situations de périphérique telles que onLowMemory()et onConfigurationChanged().

getApplicationContextrenvoie le contexte d'application global - la différence par rapport aux autres contextes est que, par exemple, un contexte d'activité peut être détruit (ou autrement rendu indisponible) par Android lorsque votre activité se termine. Le contexte d'application reste disponible pendant que votre objet Application existe (qui n'est pas lié à un spécifique Activity), vous pouvez donc l'utiliser pour des choses comme les notifications qui nécessitent un contexte qui sera disponible pendant de plus longues périodes et indépendant des objets d'interface utilisateur transitoires.

Je suppose que cela dépend de ce que fait votre code, que ceux-ci soient identiques ou non - bien qu'en utilisation normale, je m'attends à ce qu'ils soient différents.

RivieraKid
la source
19
mais an Application est un Context(il en hérite), et au moment de l'exécution, les deux méthodes retournent la même instance. Alors quelle est la différence?
Matthias
3
La différence est la portée. Votre contexte d'application sera valide beaucoup plus longtemps que, disons, un contexte d'activité, car l'activité ne peut être utilisée que pendant une très courte période, tandis que votre application peut comprendre de nombreuses activités. Votre contexte d'activité sera valide pendant au moins aussi longtemps que la durée qui commence lorsque la première activité est lancée et se termine lorsque la dernière activité. Ce sont tous des contextes, mais l'un est plus durable et ne change pas, mais d'autres sont de courte durée, et différentes instances peuvent avoir des contextes différents.
RivieraKid
16
Je pense que vous avez mal lu ma question. Je ne demande pas la différence entre un Activitycontexte et un Applicationcontexte. Je réfléchis à la différence entre Application(qui est le contexte d'application global et unique) et tout ce qui getApplicationContextrevient. Ce dernier n'était en fait pas fonctionnel avant Android 1.6; il revenait toujours null.
Matthias
1
@Matthias À mon avis, c'est toujours pertinent. Le contexte est injecté (implémenté) par le système Android lui-même, tandis que l'application hérite et étend le contexte. La classe d'application peut être facilement moquée (comme vous l'avez dit), n'est-ce pas un pari sûr qu'elle montre que la classe d'application fait de la «magie» (dans le projet de test) pour y parvenir, ignorant peut-être le contexte injecté?
Audrius
3
Répète? Je suis désolé, je ne vois toujours pas comment cela répond à ma question.
Matthias
30

Cela semble avoir à voir avec l'habillage du contexte. La plupart des classes dérivées de Contextsont en fait un ContextWrapper, qui délègue essentiellement à un autre contexte, éventuellement avec des modifications par le wrapper.

Le contexte est une abstraction générale qui prend en charge la simulation et le proxy. Étant donné que de nombreux contextes sont liés à un objet à durée de vie limitée tel qu'un Activity, il doit y avoir un moyen d'obtenir un contexte de plus longue durée, à des fins telles que l'inscription à de futures notifications. Cela est réalisé par Context.getApplicationContext(). Une implémentation logique consiste à renvoyer l' Applicationobjet global , mais rien n'empêche une implémentation de contexte de renvoyer à la place un wrapper ou un proxy avec une durée de vie appropriée.

Les activités et services sont plus spécifiquement associés à un Applicationobjet. L'utilité de cela, je crois, est que vous pouvez créer et enregistrer dans le manifeste une classe personnalisée dérivée de Applicationet être certain que Activity.getApplication()ou Service.getApplication()retournera cet objet spécifique de ce type spécifique, que vous pouvez convertir dans votre Applicationclasse dérivée et utiliser pour tout but personnalisé.

En d'autres termes, il getApplication()est garanti de renvoyer un Applicationobjet, tandis qu'il getApplicationContext()est libre de renvoyer un proxy à la place.

usethe4ce
la source
Lorsque vous dites que "le contexte est une abstraction générale qui prend en charge la moquerie et le proxy", que voulez-vous dire par "proxy" exactement? Pourriez-vous m'indiquer quelques références? Je trouve que tout le contexte est assez compliqué.
Tiago
@Tiago Cette réponse peut vous aider à mieux comprendre: stackoverflow.com/questions/10641144/…
superutilisateur
-13

Pour répondre à la question, getApplication () renvoie un objet Application et getApplicationContext () renvoie un objet Context. Sur la base de vos propres observations, je suppose que le contexte des deux est identique (c'est-à-dire que dans les coulisses, la classe Application appelle cette dernière fonction pour remplir la partie Contexte de la classe de base ou qu'une action équivalente a lieu). Peu importe la fonction que vous appelez, si vous avez juste besoin d'un contexte.

Lenny Porter
la source