Wann soll der Aktivitätskontext ODER der Anwendungskontext aufgerufen werden?

265

Es wurde viel darüber geschrieben, was diese beiden Kontexte sind. Aber ich verstehe es immer noch nicht ganz richtig

So wie ich es bisher verstehe: Jede Instanz ist eine Instanz ihrer Klasse, was bedeutet, dass einige Programmierer empfehlen, sie this.getApplicationContext()so oft wie möglich zu verwenden, um keinen Speicher zu "verlieren". Dies liegt daran, dass der andere this(Abrufen des ActivityInstanzkontexts) auf einen verweist Activity, der jedes Mal zerstört wird, wenn der Benutzer das Telefon kippt oder die App verlässt usw. Was der Garbage Collector (GC) anscheinend nicht abfängt und daher zu viel Speicher benötigt ..

Aber kann jemand bitte einige wirklich gute Codierungsbeispiele finden, bei denen es richtig wäre this(den Kontext der aktuellen ActivityInstanz abzurufen) und der Anwendungskontext nutzlos / falsch ist?

Norfeldt
quelle

Antworten:

408

getApplicationContext()ist fast immer falsch. Frau Hackborn (ua) haben sehr deutlich gewesen , dass Sie nur verwenden , getApplicationContext()wenn Sie wissen , warum Sie verwenden getApplicationContext()und nur , wenn Sie benötigen , um den EinsatzgetApplicationContext() .

Um ehrlich zu sein, verwenden "einige Programmierer" getApplicationContext()(oder getBaseContext()in geringerem Maße), weil ihre Java-Erfahrung begrenzt ist. Sie implementieren eine innere Klasse (z. B. eine OnClickListenerfür eine Buttonin einer Activity) und benötigen eine Context. Anstatt zu benutzen MyActivity.this, um an die äußere Klasse zu gelangen this, benutzen sie getApplicationContext()oder getBaseContext()um eine zu bekommenContext Objekt zu erhalten.

Sie verwenden es nur , getApplicationContext()wenn Sie wissen, dass Sie eine Contextfür etwas benötigen , das möglicherweise länger lebt als jedes andere, das ContextIhnen wahrscheinlich zur Verfügung steht. Zu den Szenarien gehören:

  • Verwenden getApplicationContext()Sie diese Option, wenn Sie etwas benötigen, das an einen Contextglobalen Gültigkeitsbereich gebunden ist . Ich verwende getApplicationContext()zum Beispiel in WakefulIntentService, damit die Statik WakeLockfür den Dienst verwendet wird. Da das WakeLockstatisch ist und ich ein brauche Context, um es PowerManagerzu erstellen, ist es am sichersten zu verwenden getApplicationContext().

  • Verwenden getApplicationContext()Sie diese Option , wenn Sie an a Servicevon a binden Activity, wenn Sie das ServiceConnection(dh das Handle an die Bindung) zwischen ActivityInstanzen über übergeben möchten onRetainNonConfigurationInstance(). Android verfolgt intern Bindungen über diese ServiceConnectionsund enthält Verweise auf Contextsdiejenigen, die die Bindungen erstellen. Wenn Sie von der binden Activity, hat die neue ActivityInstanz einen Verweis auf den, ServiceConnectionder einen impliziten Verweis auf den alten hat Activity, und der alte Activitykann nicht durch Müll gesammelt werden.

Einige Entwickler verwenden benutzerdefinierte Unterklassen von Applicationfür ihre eigenen globalen Daten, über die sie abrufen getApplicationContext(). Das ist sicherlich möglich. Ich bevorzuge statische Datenelemente, wenn Sie aus keinem anderen Grund nur ein benutzerdefiniertes ApplicationObjekt haben können. Ich habe eine App mit einem benutzerdefinierten ApplicationObjekt erstellt und fand es schmerzhaft. Frau Hackborn stimmt dieser Position ebenfalls zu .

Hier sind Gründe , warum nicht zu verwenden , getApplicationContext()wohin Sie gehen:

  • Es ist nicht vollständig Contextund unterstützt alles, was es Activitytut. Verschiedene Dinge, die Sie damit versuchen werden, schlagen Contextfehl, hauptsächlich im Zusammenhang mit der GUI .

  • Es kann zu Speicherlecks führen, wenn das Contextfrom getApplicationContext()an etwas festhält, das durch Ihre Aufrufe erstellt wurde und das Sie nicht bereinigen. Mit einem Activity, wenn es an etwas festhält Activity, spült auch alles andere aus , sobald der Müll eingesammelt ist. Das ApplicationObjekt bleibt für die Lebensdauer Ihres Prozesses erhalten.

CommonsWare
quelle
1
Vielen Dank für diese Antwort. Ein anderer Link, den ich gefunden habe, bevor ich diese Antwort gelesen habe, könnte auch einigen Leuten helfen. stackoverflow.com/questions/7298731/… - Dieser Link erklärt meine Bedenken hinsichtlich Speicherverlust.
Norfeldt
27
@Norfeldt: Zu Ihrer Information, der Link in Ihrem Kommentar verweist auf diese Antwort.
CommonsWare
2
danke .. das war der Link: stackoverflow.com/questions/5796611/… es beschreibt den Speicherverlust, den ich befürchtet hatte, wenn ich ihn benutze
Norfeldt
6
@djaqeel: Der letzte Teil Ihres Zitats ist fast wahr. Es wird besser ausgedrückt als "Geben Sie etwas, das länger als die Aktivität lebt, wie z. B. einem statischen Datenelement, keinen Aktivitätskontext". Sie verwenden jedoch immer noch nurgetApplicationContext() wenn Sie genau wissen , warum Sie es in einer bestimmten Situation benötigen. Ein Layout aufblasen? Verwenden Sie die Aktivität. Bindung an einen Dienst, bei dem Sie diese Bindung benötigen, um eine Konfigurationsänderung zu überstehen? Verwenden Sie getApplicationContext(), damit die Bindung nicht an die ActivityInstanz gebunden ist .
CommonsWare
7
@Sever: Ich beschreibe dies in meiner Antwort. Dave Smith hat auch einen ausgezeichneten Blog-Beitrag über Kontexte: doubleencore.com/2013/06/context Sein zusammenfassender Absatz: "Verwenden Sie in den meisten Fällen den Kontext, der Ihnen direkt von der einschließenden Komponente zur Verfügung steht, in der Sie arbeiten. Sie können ihn sicher halten Ein Verweis darauf, solange dieser Verweis nicht über den Lebenszyklus dieser Komponente hinausgeht. Sobald Sie einen Verweis auf einen Kontext von einem Objekt speichern müssen, das außerhalb Ihrer Aktivität oder Ihres Dienstes liegt, wechseln Sie den von Ihnen gespeicherten Verweis auch vorübergehend über den Anwendungskontext. "
CommonsWare
48

Ich denke, es gibt eine Menge Dinge, die auf der SDK-Site schlecht dokumentiert sind. Dies ist eine davon. Die Behauptung, die ich machen werde, ist, dass es anscheinend besser ist, standardmäßig einen Anwendungskontext zu verwenden und einen Aktivitätskontext nur dann zu verwenden, wenn Sie es wirklich brauchen. Der einzige Ort, an dem ich jemals gesehen habe, dass Sie einen Aktivitätskontext benötigen, ist ein Fortschrittsdialog. SBERG412 behauptet, dass Sie einen Aktivitätskontext für eine Toastnachricht verwenden müssen, die Android-Dokumente zeigen jedoch deutlich, dass ein Anwendungskontext verwendet wird. Aufgrund dieses Google-Beispiels habe ich immer den Anwendungskontext für Toasts verwendet. Wenn dies falsch ist, hat Google den Ball hier abgelegt.

Hier ist mehr zum Nachdenken und Überprüfen:

Für eine Toastnachricht verwendet der Google Dev Guide den Anwendungskontext und sagt ausdrücklich, dass er verwendet werden soll: Toastbenachrichtigungen

Im Abschnitt "Dialoge" des Dev-Handbuchs sehen Sie, dass ein AlertDialog.Builder den Anwendungskontext verwendet und der Fortschrittsbalken einen Aktivitätskontext. Dies wird von Google nicht erklärt. Dialoge

Ein guter Grund für die Verwendung des Anwendungskontexts scheint zu sein, wenn Sie Konfigurationsänderungen wie eine Orientierungsänderung behandeln und Objekte beibehalten möchten, die einen Kontext wie Ansichten benötigen. Wenn Sie hier suchen: Laufzeitänderungen Verwendung eines Aktivitätskontexts, der zu einem Leck führen kann, ist Vorsicht geboten. Dies kann mit einem Anwendungskontext mit den Ansichten vermieden werden, die beibehalten werden sollen (zumindest ist das mein Verständnis). In einer App, die ich schreibe, beabsichtige ich, einen Anwendungskontext zu verwenden, da ich versuche, einige Ansichten und andere Dinge bei einer Orientierungsänderung beizubehalten, und ich möchte weiterhin, dass die Aktivität bei Orientierungsänderungen zerstört und neu erstellt wird. Daher muss ich einen App-Kontext verwenden, um keinen Speicherverlust zu verursachen (siehe Vermeiden von Speicherverlusten) ). Mir scheint, es gibt viele gute Gründe, den Anwendungskontext anstelle eines Aktivitätskontexts zu verwenden, und für mich scheint es fast so, als würden Sie ihn häufiger als einen Aktivitätskontext verwenden. Das scheinen viele Android-Bücher zu tun, und genau das tun viele der Google-Beispiele, die ich gesehen habe.

Die Google-Dokumentation lässt den Eindruck entstehen, dass die Verwendung des Anwendungskontexts in den meisten Fällen vollkommen in Ordnung ist und tatsächlich häufiger als die Verwendung eines Aktivitätskontexts in ihren Beispielen (zumindest in den Beispielen, die ich gesehen habe). Wenn es wirklich so ein Problem ist, den Anwendungskontext zu verwenden, muss Google dies wirklich stärker betonen. Sie müssen es klarstellen und einige ihrer Beispiele wiederholen. Ich würde dies nicht nur unerfahrenen Entwicklern vorwerfen, da die Behörde (Google) es wirklich so aussehen lässt, als sei es kein Problem, Anwendungskontexte zu verwenden.

Andi Jay
quelle
5
Ich stimme vollkommen zu. Die Antwort von CommonsWare war für mich eine kleine Überraschung. Ich bin froh, dass ich diese Frage gefunden habe, da die Google-Dokumentation darauf hinweist, dass die Verwendung von getApplicationContext so gefährlich sein kann.
Steve Schwarcz
38

Ich habe diese Tabelle als Anleitung für die Verwendung der verschiedenen Kontexttypen verwendet, z. B. Anwendungskontext (dh :) getApplicationContext()und Aktivitätskontext , auch BroadcastReceiver-Kontext :

Geben Sie hier die Bildbeschreibung ein

Alle Verdienste gehen an den ursprünglichen Autor hier für weitere Informationen.

CommonSenseCode
quelle
11

Welcher Kontext soll verwendet werden?

Es gibt zwei Arten von Kontext:

  1. Der Anwendungskontext ist mit der Anwendung verknüpft und bleibt während der gesamten Lebensdauer der Anwendung gleich - er ändert sich nicht. Wenn Sie also Toast verwenden, können Sie den Anwendungskontext oder sogar den Aktivitätskontext (beide) verwenden, da Toast von überall in Ihrer Anwendung angezeigt werden kann und nicht an ein bestimmtes Fenster angehängt ist. Es gibt jedoch viele Ausnahmen. Eine Ausnahme ist, wenn Sie den Aktivitätskontext verwenden oder übergeben müssen.

  2. Der Aktivitätskontext ist mit der Aktivität verknüpft und kann zerstört werden, wenn die Aktivität zerstört wird. Es kann mehrere Aktivitäten (mehr als wahrscheinlich) mit einer einzigen Anwendung geben. Und manchmal benötigen Sie unbedingt das Aktivitätskontext-Handle. Wenn Sie beispielsweise eine neue Aktivität starten, müssen Sie den Aktivitätskontext in seiner Absicht verwenden, damit die neue Startaktivität in Bezug auf den Aktivitätsstapel mit der aktuellen Aktivität verbunden ist. Sie können jedoch auch den Kontext der Anwendung verwenden, um eine neue Aktivität zu starten. Anschließend müssen Sie das Flag setzen Intent.FLAG_ACTIVITY_NEW_TASK, um sie als neue Aufgabe zu behandeln.

Betrachten wir einige Fälle:

  • MainActivity.this bezieht sich auf den MainActivity-Kontext, der die Activity-Klasse erweitert, aber die Basisklasse (Aktivität) erweitert auch die Context-Klasse, sodass sie zum Anbieten des Aktivitätskontexts verwendet werden kann.

  • getBaseContext() bietet Aktivitätskontext.

  • getApplication() bietet Anwendungskontext.

  • getApplicationContext() bietet auch Anwendungskontext.

Weitere Informationen finden Sie unter diesem Link .

Zohra Khan
quelle
Was ist mit dem Fall, in dem ein AlertDialog in der App angezeigt werden muss, z. B. ein asynchroner Prozess, der ein Ergebnis anzeigt? Ein Beispiel hierfür kann sein : Der Benutzer klickt auf den Download, dies löst eine Download-Anfrage für aus downloadmanagerund wenn das fertige Signal empfangen wird, sollte ein Dialogfeld angezeigt werden, z. B. "Was möchten Sie mit diesem Download tun?". Meine (Hack-) Lösung speichert die aktuellste Activityin einer static ApplicationKlasse und fordert die aktuelle an, Activitywenn der Download abgeschlossen ist. Ich bezweifle jedoch, dass dies die richtige Implementierung ist. TL; DR Wie kann AlertDialog irgendwo in der App angezeigt werden?
CybeX
@KGCybeX Wenn Sie nach Abschluss des Downloads irgendetwas und irgendwo in Ihrer App anzeigen möchten, sollten Sie manuell einen Rundfunkempfänger für Ihre Aktivität registrieren, der auf eine bestimmte Nachricht wartet, die Ihr Download-Dienst sendet, und nach Erhalt der Nachricht tun, was Sie wollen, oder anhängen Ihre Aktivität zu diesem Dienst direkt.
ExiRouS
6

Ich habe mich gefragt, warum ich den Anwendungskontext nicht für jede unterstützte Operation verwenden soll. Am Ende wird die Wahrscheinlichkeit eines Speicherverlusts und einer fehlenden Nullprüfung für getContext () oder getActivity () verringert (wenn ein injizierter Anwendungskontext verwendet oder durch eine statische Methode von Application erfasst wird). Aussagen, wie die von Frau Hackborn , den Anwendungskontext nur bei Bedarf zu verwenden, scheinen für mich nicht überzeugend zu sein, ohne eine Erklärung dafür zu haben. Aber es scheint, dass ich ein Unkleid gefunden habe, warum:

haben festgestellt, dass es bei einigen Android-Versionen / Gerätekombinationen Probleme gibt, die diesen Regeln nicht entsprechen. Wenn ich beispielsweise einen BroadcastReceiver habe, dem ein Kontext übergeben wird, und diesen Kontext in einen Anwendungskontext konvertiere und dann versuche, registerReceiver () im Anwendungskontext aufzurufen, gibt es viele Fälle, in denen dies gut funktioniert, aber auch viele Fälle, in denen ich erhalte ein Absturz aufgrund einer ReceiverCallNotAllowedException. Diese Abstürze treten bei einer Vielzahl von Android-Versionen von API 15 bis 22 auf. Https://possiblemobile.com/2013/06/context/#comment-2443283153

Da nicht garantiert werden kann, dass alle in der folgenden Tabelle vom Anwendungskontext unterstützten Vorgänge auf allen Android-Geräten funktionieren! Geben Sie hier die Bildbeschreibung ein

Malachiasz
quelle
4

Zwei gute Beispiele dafür, wann Sie den Aktivitätskontext im Vergleich zum Anwendungskontext verwenden sollten, sind das Anzeigen einer Toastnachricht oder einer integrierten Dialognachricht, da die Verwendung des Anwendungskontexts eine Ausnahme verursacht:

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

oder

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

Beide benötigen Informationen aus dem Aktivitätskontext, die nicht im Anwendungskontext bereitgestellt werden.

SBerg413
quelle
5
Hm .. Welche Android OS Version hast du getestet? Ich habe auf 4.4.4 getestet und es funktioniert gut. Wie @Andi Jay erwähnte, verwendete das offizielle Android-Entwicklerdokument den Anwendungskontext in seinem Beispielcode. developer.android.com/guide/topics/ui/notifiers/…
김준호
1
@Chinesischer Name, ja, es könnte funktionieren, aber irgendwann in der Zukunft dieser App wird es auch abstürzen. Ist mir mehrmals passiert.
Ojonugwa Jude Ochalifu
1
Wenn ich den Aktivitätskontext in Toast verwende, geht Speicher verloren!
Jemshit Iskenderov
3

Anwendungskontext live, bis Ihre Anwendung nur noch aktiv ist und nicht vom Aktivitätslebenszyklus abhängt, sondern der Kontext das Objekt langlebig hält . Wenn das Objekt, das Sie temporär verwenden, in dieser Zeit den Anwendungskontext und den Aktivitätskontext verwendet, wird der Anwendungskontext vollständig verwendet.

Ganesh Katikar
quelle