getApplication () vs. getApplicationContext ()

417

Ich konnte keine zufriedenstellende Antwort darauf finden, also los geht's: Was ist los mit Activity/Service.getApplication()undContext.getApplicationContext() ?

In unserer Anwendung geben beide dasselbe Objekt zurück. In einem Fall wird durch das ActivityTestCaseVerspotten der Anwendung jedoch das Verspotten getApplication()zurückgegeben, es getApplicationContextwird jedoch immer noch eine andere Kontextinstanz zurückgegeben (eine von Android injizierte). Ist das ein Fehler? Ist es absichtlich?

Ich verstehe den Unterschied überhaupt nicht. Gibt es Fälle außerhalb einer Testsuite, in denen beide Anrufe mit unterschiedlichen Objekten zurückkommen können? Wann und warum? Warum ist außerdem getApplicationauf Activityund definiert Service, aber nicht auf Context? Sollte nicht immer eine gültige Anwendungsinstanz von überall verfügbar sein ?

Matthias
quelle
8
Gute Frage. Das Testen ist ein bisschen rätselhaft (wie Sie wissen). Ich frage mich jedoch, ob sich in diesen beiden Methodenaufrufen ein Unterschied manifestiert, wenn Sie in Ihrer App keinApplication Objekt explizit erstellen .
Christopher Orr

Antworten:

366

Sehr interessante Frage. Ich denke, es ist hauptsächlich eine semantische Bedeutung und kann auch historische Gründe haben.

Obwohl in aktuellen Implementierungen Android Aktivität und Dienstleistung, getApplication()und getApplicationContext()das gleiche Objekt zurück, gibt es keine Garantie , dass dies immer der Fall sein (beispielsweise in einer bestimmten Lieferanten - Implementierung).

Wenn Sie also die Anwendungsklasse möchten, die Sie im Manifest registriert haben, sollten Sie sie niemals aufrufen getApplicationContext()und in Ihre Anwendung umwandeln, da es sich möglicherweise nicht um die Anwendungsinstanz handelt (die Sie offensichtlich mit dem Testframework erlebt haben).

Warum gibt getApplicationContext()es überhaupt?

getApplication()ist nur in der Activity-Klasse und der Service-Klasse verfügbar, während sie getApplicationContext()in der Context-Klasse deklariert ist.

Das bedeutet tatsächlich eines: Wenn Sie Code in einen Rundfunkempfänger schreiben, der kein Kontext ist, sondern in seiner onReceive-Methode einen Kontext erhält, können Sie nur aufrufen getApplicationContext(). Dies bedeutet auch, dass Sie in einem BroadcastReceiver nicht garantiert Zugriff auf Ihre Anwendung haben.

Wenn Sie sich den Android-Code ansehen, sehen Sie, dass eine Aktivität im Anhang einen Basiskontext und eine Anwendung erhält. Dies sind unterschiedliche Parameter. getApplicationContext()delegiert den Anruf an baseContext.getApplicationContext().

Eine weitere Sache: Die Dokumentation besagt, dass Sie in den meisten Fällen keine Unterklasse für die Anwendung benötigen sollten:

Normalerweise ist keine Unterklasse erforderlich Application. In den meisten Situationen können statische Singletons dieselbe Funktionalität auf modularere Weise bereitstellen. Wenn Ihr Singleton einen globalen Kontext benötigt (zum Beispiel zum Registrieren von Rundfunkempfängern), kann der Funktion zum Abrufen eine gegeben werden, Contextdie Context.getApplicationContext()beim ersten Erstellen des Singletons intern verwendet wird .

Ich weiß, dass dies keine genaue und präzise Antwort ist, aber beantwortet das trotzdem Ihre Frage?

Pierre-Yves Ricau
quelle
89
@ Piwaï: Hör nicht auf das Dokument. Unterklassen android.app.Applicationsind super Hilfe voll. Zum Beispiel hatte ich endlose Probleme beim Initialisieren der Datenbank. Einmal eingezogen Application.onCreate, funktionierte es wie ein Zauber. Jetzt mache ich alle systemweiten Initialisierungen in Applicationund würde ohne keine weitere App schreiben.
Martin
9
@Martin Wenn Sie die Dokumente nicht anhören, kann es sein, dass Ihr Code in Zukunft oder sogar jetzt unter unerwarteten Bedingungen beschädigt wird, die Portabilität verliert, eine schlechte Leistung erbringt und Plattformentwickler daran hindert, eine vorteilhafte Änderung vorzunehmen (dies verstößt gegen die Annahme, die Sie falsch gemacht haben, obwohl dies der Fall war basiert nur auf der aktuellen Implementierung, nicht auf den Dokumenten). Ich denke, dass dies ein ziemlich schlechtes Verhalten und ein ziemlich schlechter Ratschlag ist.
Palec
17
@Palec: "Normalerweise muss die Anwendung nicht in Unterklassen unterteilt werden." - Das ist nur ein Hinweis. Ich verwende weiterhin offiziell dokumentierte Funktionen in der vorgesehenen Weise. - Ich habe diese „statischen Singletons“ am Anfang verwendet und sie erwiesen sich als Schmerz in der a… - faulen Initialisierung hat ihre Probleme. Besonders bei Instrumentierungstests. - Ich habe immer noch diese Singletons für die Modularität, aber ich instanziiere sie en block in der onCreate einer android.app.Application-Unterklasse. - klappt wunderbar.
Martin
9
@ Martin Ich hätte klarstellen sollen: Meine Reaktion betraf nur den ersten Satz. "Hören Sie nicht auf das Dokument." Dies ist im Allgemeinen ein sehr gefährlicher Ratschlag. Aber "Dies ist nur ein Hinweis - Sie können das Dokument in diesem Fall ignorieren, wenn Sie einen Grund haben und ich werde Ihnen einen zeigen ..." klingt für mich absolut in Ordnung.
Palec
3
"Wenn Sie Code in einen Broadcast-Empfänger schreiben, der kein Kontext ist, sondern in seiner onReceive-Methode einen Kontext erhält, können Sie nur getApplicationContext () aufrufen. Dies bedeutet auch, dass Sie in einem BroadcastReceiver NICHT garantiert Zugriff auf Ihre Anwendung haben. "" Was können wir also tun, um in BroadcastReceiver auf meine Anwendungsklasse zuzugreifen?
Dr.jacky
30

Vergleiche getApplication()und getApplicationContext().

getApplicationgibt ein ApplicationObjekt , das Sie zu verwalten Ihre globalen Anwendungszustand und reagieren auf einige Geräte Situationen wie erlaubt onLowMemory()und onConfigurationChanged().

getApplicationContextGibt den globalen Anwendungskontext zurück. Der Unterschied zu anderen Kontexten besteht darin, dass beispielsweise ein Aktivitätskontext von Android zerstört (oder anderweitig nicht verfügbar gemacht) werden kann, wenn Ihre Aktivität endet. Der Anwendungskontext bleibt verfügbar, solange Ihr Anwendungsobjekt vorhanden ist (das nicht an ein bestimmtes Objekt gebunden ist Activity), sodass Sie ihn beispielsweise für Benachrichtigungen verwenden können , für die ein Kontext erforderlich ist, der für längere Zeiträume verfügbar und unabhängig von vorübergehenden UI-Objekten ist.

Ich denke, es hängt davon ab, was Ihr Code tut, ob diese gleich sind oder nicht - obwohl ich bei normaler Verwendung erwarten würde, dass sie unterschiedlich sind.

RivieraKid
quelle
19
aber an Application ist a Context(es erbt davon) und zur Laufzeit geben beide Methoden dieselbe Instanz zurück. Was ist der Unterschied?
Matthias
3
Der Unterschied ist der Umfang. Ihr Anwendungskontext ist weitaus länger gültig als beispielsweise ein Aktivitätskontext, da die Aktivität möglicherweise nur für eine sehr kurze Zeit verwendet wird, während Ihre Anwendung möglicherweise aus vielen Aktivitäten besteht. Ihr Aktivitätskontext ist mindestens so lange gültig wie die Dauer, die mit dem Start der ersten Aktivität beginnt und mit der letzten Aktivität endet. Sie sind alle Kontexte, aber einer ist länger anhaltend und ändert sich nicht, aber andere sind kurzlebig und verschiedene Instanzen können unterschiedliche Kontexte haben.
RivieraKid
16
Ich denke, Sie könnten meine Frage falsch verstehen. Ich frage nicht nach dem Unterschied zwischen einem ActivityKontext und einem ApplicationKontext. Ich denke über den Unterschied zwischen Application(dem globalen, einzigartigen Anwendungskontext) und dem, was getApplicationContextzurückkommt, nach. Letzteres war vor Android 1.6 tatsächlich nicht funktionsfähig; es kehrte immer zurück null.
Matthias
1
@ Matthias Meiner Meinung nach ist es immer noch relevant. Der Kontext wird vom Android-System selbst injiziert (implementiert), während die Anwendung den Kontext erbt und erweitert. Die Anwendungsklasse kann leicht verspottet werden (wie von Ihnen gesagt). Ist es dann nicht sicher, dass sie zeigt, dass die Anwendungsklasse (im Testprojekt) etwas "Magie" ausführt, um dies zu erreichen, und möglicherweise den injizierten Kontext ignoriert?
Audrius
3
Komm wieder? Es tut mir leid, ich sehe immer noch nicht, wie das meine Frage beantwortet.
Matthias
30

Es scheint mit Kontextumbruch zu tun zu haben. Die meisten abgeleiteten Klassen Contextsind tatsächlich a ContextWrapper, die im Wesentlichen an einen anderen Kontext delegieren, möglicherweise mit Änderungen durch den Wrapper.

Der Kontext ist eine allgemeine Abstraktion, die das Verspotten und Proxying unterstützt. Da viele Kontexte an ein Objekt mit begrenzter Lebensdauer wie z. B. a gebunden sind Activity, muss es eine Möglichkeit geben, einen längerlebigen Kontext zu erhalten, z. B. um sich für zukünftige Benachrichtigungen zu registrieren. Das wird erreicht durch Context.getApplicationContext(). Eine logische Implementierung besteht darin, das globale ApplicationObjekt zurückzugeben, aber nichts hindert eine Kontextimplementierung daran, stattdessen einen Wrapper oder Proxy mit einer geeigneten Lebensdauer zurückzugeben.

Aktivitäten und Dienstleistungen sind spezifischer mit einem ApplicationObjekt verbunden. Die Nützlichkeit dieses, glaube ich, ist , dass Sie in dem Manifest einer benutzerdefinierten Klasse erstellen und registrieren können abgeleitet Applicationund sicher sein , dass Activity.getApplication()oder Service.getApplication()werden , dass bestimmtes Objekt dieses speziellen Typen zurückgeben, die Sie Ihren abgeleitet werfen können ApplicationKlasse und Verwendung für was auch immer kundenspezifischer Zweck.

Mit anderen Worten, es getApplication()wird garantiert, dass ein ApplicationObjekt zurückgegeben wird, während getApplicationContext()es frei ist, stattdessen einen Proxy zurückzugeben.

usethe4ce
quelle
Wenn Sie sagen "Der Kontext ist eine allgemeine Abstraktion, die das Verspotten und Proxying unterstützt", was meinen Sie dann genau mit "Proxying"? Könnten Sie mich auf einige Referenzen verweisen? Ich finde die ganze Kontextsache ziemlich verworren.
Tiago
@Tiago Diese Antwort kann Ihnen helfen, besser zu verstehen: stackoverflow.com/questions/10641144/…
Superuser
-13

Um die Frage zu beantworten, gibt getApplication () ein Anwendungsobjekt und getApplicationContext () ein Kontextobjekt zurück. Basierend auf Ihren eigenen Beobachtungen würde ich annehmen, dass der Kontext von beiden identisch ist (dh hinter den Kulissen ruft die Anwendungsklasse die letztere Funktion auf, um den Kontextteil der Basisklasse zu füllen, oder es findet eine gleichwertige Aktion statt). Es sollte eigentlich keine Rolle spielen, welche Funktion Sie aufrufen, wenn Sie nur einen Kontext benötigen.

Lenny Porter
quelle