Ich habe diese Antwort im Jahr '09 geschrieben, als Android noch relativ neu war und es viele noch nicht gut etablierte Bereiche in der Android-Entwicklung gab. Ich habe einen langen Nachtrag am Ende dieses Beitrags hinzugefügt, in dem ich auf einige Kritikpunkte eingehe und eine philosophische Meinungsverschiedenheit mit der Verwendung von Singletons anstelle der Unterklassifizierung von Anwendungen darlege. Lesen Sie ihn auf eigene Gefahr.
URSPRÜNGLICHE ANTWORT:
Das allgemeinere Problem, auf das Sie stoßen, ist die Frage, wie Sie den Status über mehrere Aktivitäten und alle Teile Ihrer Anwendung hinweg speichern können. Eine statische Variable (z. B. ein Singleton) ist eine gängige Java-Methode, um dies zu erreichen. Ich habe jedoch festgestellt, dass ein eleganterer Weg in Android darin besteht, den Status mit dem Anwendungskontext zu verknüpfen.
Wie Sie wissen, ist jede Aktivität auch ein Kontext, der Informationen über ihre Ausführungsumgebung im weitesten Sinne enthält. Auch Ihre Anwendung hat einen Kontext, und Android garantiert, dass er in Ihrer gesamten Anwendung als eine einzige Instanz existiert.
Um dies zu tun, erstellen Sie Ihre eigene Unterklasse von Android.app.Anwendung und geben Sie diese Klasse dann im Anwendungs-Tag in Ihrem Manifest an. Jetzt erstellt Android automatisch eine Instanz dieser Klasse und macht sie für Ihre gesamte Anwendung verfügbar. Sie können sie von jedem context
unter Verwendung der Context.getApplicationContext()
Methode ( Activity
bietet auch eine Methode getApplication()
was genau die gleiche Wirkung hat). Es folgt ein stark vereinfachtes Beispiel, für das es einige Vorbehalte gibt:
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
Dies hat im Wesentlichen die gleiche Wirkung wie die Verwendung einer statischen Variable oder eines Singletons, fügt sich aber recht gut in das bestehende Android-Framework ein. Beachten Sie, dass dies nicht prozessübergreifend funktioniert (sollte Ihre App eine der wenigen sein, die mehrere Prozesse hat).
Ein Hinweis auf das obige Beispiel: Nehmen wir an, wir hätten stattdessen etwas wie folgt getan:
class MyApp extends Application {
private String myState = /* complicated and slow initialization */;
public String getState(){
return myState;
}
}
Jetzt wird diese langsame Initialisierung (wie z.B. auf die Festplatte zugreifen, auf das Netzwerk zugreifen, irgendetwas blockieren, usw.) jedes Mal durchgeführt, wenn die Anwendung instanziert wird! Sie werden vielleicht denken, na ja, das ist ja nur einmalig für den Prozess und ich muss die Kosten ja sowieso bezahlen, oder? Wie Dianne Hackborn weiter unten erwähnt, ist es zum Beispiel durchaus möglich, dass Ihr Prozess instanziiert wird - nur - um ein Broadcast-Ereignis im Hintergrund zu verarbeiten. Wenn Ihre Broadcast-Verarbeitung keinen Bedarf für diesen Zustand hat, haben Sie möglicherweise eine ganze Reihe komplizierter und langsamer Operationen umsonst durchgeführt. Faule Instanziierung ist hier das Gebot der Stunde. Im Folgenden wird eine etwas kompliziertere Art der Verwendung von Application vorgestellt, die für alles andere als die einfachsten Anwendungen sinnvoller ist:
class MyApp extends Application {
private MyStateManager myStateManager = new MyStateManager();
public MyStateManager getStateManager(){
return myStateManager ;
}
}
class MyStateManager {
MyStateManager() {
/* this should be fast */
}
String getState() {
/* if necessary, perform blocking calls here */
/* make sure to deal with any multithreading/synchronicity issues */
...
return state;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
String state = stateManager.getState();
...
}
}
Ich bevorzuge zwar die Unterklassifizierung von Anwendungen gegenüber der Verwendung von Singletons, da dies die elegantere Lösung ist, aber ich würde es vorziehen, wenn Entwickler Singletons verwenden, wenn es wirklich notwendig ist, und nicht über die Auswirkungen auf die Leistung und das Multithreading nachdenken, wenn sie den Zustand mit der Unterklasse Anwendung verknüpfen.
ANMERKUNG 1: Auch als anticafe kommentiert, um richtig binden Sie Ihre Anwendung überschreiben, um Ihre Anwendung ein Tag ist in der Manifestdatei erforderlich. Auch hier finden Sie in den Android-Dokumenten weitere Informationen. Ein Beispiel:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
ANMERKUNG 2: user608578 fragt unten, wie das mit der Verwaltung von nativen Objektlebenszyklen funktioniert. Ich bin nicht auf dem Laufenden über die Verwendung von nativen Code mit Android in der geringsten, und ich bin nicht qualifiziert zu beantworten, wie das mit meiner Lösung interagieren würde. Wenn jemand eine Antwort darauf hat, bin ich bereit, sie zu nennen und die Informationen in diesem Beitrag für maximale Sichtbarkeit zu veröffentlichen.
ADDENDUM:
Wie einige Leute festgestellt haben, ist dies no eine Lösung für beständig Das hätte ich vielleicht in meiner ursprünglichen Antwort stärker betonen sollen. D.h. dies ist nicht als Lösung für die Speicherung von Benutzer- oder anderen Informationen gedacht, die über die Lebenszeit der Anwendung hinweg erhalten bleiben sollen. Daher betrachte ich die meiste Kritik, die sich darauf bezieht, dass Anwendungen jederzeit beendet werden können usw., als überflüssig, da alles, was jemals auf der Festplatte gespeichert werden sollte, nicht über eine Anwendungsunterklasse gespeichert werden sollte. Sie ist als Lösung für die Speicherung von temporären, leicht wiederherstellbaren Anwendungszuständen (z. B. ob ein Benutzer angemeldet ist) und Komponenten gedacht, die nur eine Instanz darstellen (z. B. Anwendungsnetzwerkmanager) ( NICHT Singleton!) in der Natur.
Dayerman war so freundlich, auf ein interessantes Thema hinzuweisen Gespräch mit Reto Meier und Dianne Hackborn in denen von der Verwendung von Anwendungsunterklassen zugunsten von Singleton-Mustern abgeraten wird. Auch Somatik hat bereits auf etwas in dieser Richtung hingewiesen, obwohl ich es damals nicht gesehen habe. Aufgrund der Rolle von Reto und Dianne bei der Pflege der Android-Plattform kann ich in gutem Glauben nicht empfehlen, ihre Ratschläge zu ignorieren. Was sie sagen, gilt. Ich möchte jedoch den Meinungen widersprechen, die in Bezug auf die Bevorzugung von Singleton gegenüber Anwendungsunterklassen geäußert wurden. In meiner Ablehnung werde ich mich auf Konzepte stützen, die am besten in diese StackExchange-Erklärung des Singleton-Entwurfsmusters damit ich in dieser Antwort keine Begriffe definieren muss. Ich empfehle dringend, den Link zu überfliegen, bevor Sie fortfahren. Punkt für Punkt:
Dianne erklärt: "Es gibt keinen Grund, den Antrag als Unterkategorie zu behandeln. Es ist nicht anders, als ein Singleton zu machen..." Diese erste Behauptung ist falsch. Dafür gibt es zwei Hauptgründe. 1) Die Klasse Application bietet eine bessere Lebensdauergarantie für einen Anwendungsentwickler; es ist garantiert, dass sie die Lebensdauer der Anwendung hat. Ein Singleton ist nicht EXPLICITLY an die Lebensdauer der Anwendung gebunden (obwohl es das effektiv ist). Dies mag für den durchschnittlichen Anwendungsentwickler kein Problem darstellen, aber ich würde behaupten, dass dies genau die Art von Vertrag ist, die die Android-API anbieten sollte, und es bietet dem Android-System auch viel mehr Flexibilität, indem es die Lebensdauer der zugehörigen Daten minimiert. 2) Die Anwendungsklasse bietet dem Anwendungsentwickler einen einzelnen Instanzhalter für den Zustand, der sich stark von einem Singleton-Halter für den Zustand unterscheidet. Eine Liste der Unterschiede finden Sie unter dem obigen Link zur Erklärung von Singleton.
Dianne fährt fort: "...nur etwas, das Sie in der Zukunft wahrscheinlich bereuen werden, wenn Sie feststellen, dass Ihr Anwendungsobjekt zu einem großen Durcheinander von etwas wird, das eigentlich unabhängige Anwendungslogik sein sollte." Das ist sicherlich nicht falsch, aber es ist kein Grund, Singleton einer Anwendungsunterklasse vorzuziehen. Keines von Dianes Argumenten liefert einen Grund dafür, dass die Verwendung eines Singletons besser ist als eine Anwendungs-Subklasse, alles, was sie zu beweisen versucht, ist, dass die Verwendung eines Singletons nicht schlechter ist als eine Anwendungs-Subklasse, was meiner Meinung nach falsch ist.
Sie fährt fort: "Und das führt ganz natürlich dazu, wie man diese Dinge verwalten sollte - indem man sie bei Bedarf initialisiert." Dies ignoriert die Tatsache, dass es keinen Grund gibt, warum man nicht auch mit einer Anwendungsunterklasse bei Bedarf initialisieren kann. Auch hier gibt es keinen Unterschied.
Dianne endet mit "Das Framework selbst hat tonnenweise Singletons für all die kleinen gemeinsam genutzten Daten, die es für die App verwaltet, wie z. B. Caches für geladene Ressourcen, Pools für Objekte usw. It works great." Ich behaupte nicht, dass die Verwendung von Singletons nicht gut funktionieren kann oder keine legitime Alternative ist. Ich behaupte, dass Singletons keinen so starken Vertrag mit dem Android-System bieten wie die Verwendung einer Anwendungsunterklasse, und dass die Verwendung von Singletons im Allgemeinen auf ein unflexibles Design hinweist, das nicht leicht zu ändern ist und zu vielen Problemen auf dem Weg dahin führt. IMHO ist der starke Vertrag, den die Android-API den Entwickleranwendungen bietet, einer der attraktivsten und angenehmsten Aspekte der Programmierung mit Android und hat dazu beigetragen, dass die Android-Plattform schon früh von den Entwicklern angenommen wurde, was sie zu ihrem heutigen Erfolg geführt hat. Der Vorschlag, Singletons zu verwenden, bedeutet implizit eine Abkehr von einem starken API-Vertrag und schwächt meiner Meinung nach das Android-Framework.
Dianne hat weiter unten auch einen Kommentar abgegeben, in dem sie auf einen weiteren Nachteil der Verwendung von Anwendungsunterklassen hinweist: Sie können dazu ermutigen oder es erleichtern, weniger leistungsfähigen Code zu schreiben. Das ist sehr wahr, und ich habe diese Antwort bearbeitet, um zu betonen, wie wichtig es ist, hier die Leistung zu berücksichtigen und den richtigen Ansatz zu wählen, wenn Sie Anwendungsunterklassen verwenden. Wie Dianne sagt, ist es wichtig, sich daran zu erinnern, dass Ihre Anwendungsklasse jedes Mal instanziiert wird, wenn Ihr Prozess geladen wird (das kann mehrfach der Fall sein, wenn Ihre Anwendung in mehreren Prozessen läuft!), selbst wenn der Prozess nur für ein Broadcast-Ereignis im Hintergrund geladen wird. Es ist daher wichtig, die Anwendungsklasse eher als Repository für Zeiger auf gemeinsam genutzte Komponenten Ihrer Anwendung zu verwenden und nicht als Ort, an dem die Verarbeitung stattfindet!
Ich lasse Sie mit der folgenden Liste der Nachteile von Singletons, wie aus dem früheren StackExchange Link gestohlen:
- Unfähigkeit, abstrakte oder Schnittstellenklassen zu verwenden;
- Unfähigkeit zur Unterklassifizierung;
- Hohe Kopplung in der gesamten Anwendung (schwer zu ändern);
- Schwierig zu testen (kann nicht in Unit-Tests gefälscht/geprüft werden);
- Schwierige Parallelisierung im Falle eines veränderlichen Zustands (erfordert umfangreiches Sperren);
und meine eigenen hinzufügen:
- Unklarer und unüberschaubarer Vertrag auf Lebenszeit, der für die Entwicklung von Android (oder den meisten anderen Geräten) ungeeignet ist;