Ich habe diese Antwort bereits '09 geschrieben, als Android noch relativ neu war und es in der Android-Entwicklung viele nicht gut etablierte Bereiche gab. Ich habe am Ende dieses Beitrags einen langen Nachtrag hinzugefügt, in dem einige Kritikpunkte angesprochen und eine philosophische Meinungsverschiedenheit über die Verwendung von Singletons anstelle der Unterklassifizierung von Application beschrieben werden. Lesen Sie es auf eigenes Risiko.
URSPRÜNGLICHE ANTWORT:
Das allgemeinere Problem, auf das Sie stoßen, ist das Speichern des Status über mehrere Aktivitäten und alle Teile Ihrer Anwendung hinweg. Eine statische Variable (z. B. ein Singleton) ist eine gängige Java-Methode, um dies zu erreichen. Ich habe jedoch festgestellt, dass eine elegantere Möglichkeit in Android darin besteht, Ihren 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. Ihre Anwendung hat auch einen Kontext, und Android garantiert, dass sie in Ihrer Anwendung als einzelne Instanz vorhanden ist.
Um dies zu tun, erstellen Sie eine eigene Unterklasse von android.app.Application und geben diese Klasse im Anwendungs-Tag in Ihrem Manifest an. Jetzt erstellt Android automatisch eine Instanz dieser Klasse und stellt sie für Ihre gesamte Anwendung zur Verfügung. Sie können von jedem context
mit der Context.getApplicationContext()
Methode darauf zugreifen ( Activity
bietet auch eine Methode getApplication()
, die genau den gleichen Effekt hat). Das Folgende ist ein extrem vereinfachtes Beispiel mit folgenden Einschränkungen:
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 den gleichen Effekt wie die Verwendung einer statischen Variablen oder eines Singletons, lässt sich jedoch recht gut in das vorhandene Android-Framework integrieren. Beachten Sie, dass dies nicht prozessübergreifend funktioniert (sollte Ihre App eine der seltenen mit mehreren Prozessen sein).
Aus dem obigen Beispiel ist etwas zu beachten. Angenommen, wir hätten stattdessen etwas getan wie:
class MyApp extends Application {
private String myState = /* complicated and slow initialization */;
public String getState(){
return myState;
}
}
Jetzt wird diese langsame Initialisierung (wie das Schlagen der Festplatte, das Schlagen des Netzwerks, alles Blockieren usw.) jedes Mal durchgeführt, wenn die Anwendung instanziiert wird! Sie denken vielleicht, das ist nur einmal für den Prozess und ich muss die Kosten trotzdem bezahlen, oder? Wie Dianne Hackborn weiter unten erwähnt, ist es beispielsweise durchaus möglich, dass Ihr Prozess instanziiert wird, um ein Hintergrund-Broadcast-Ereignis zu verarbeiten. Wenn Ihre Broadcast-Verarbeitung diesen Status nicht benötigt, haben Sie möglicherweise nur eine ganze Reihe komplizierter und langsamer Vorgänge umsonst ausgeführt. Lazy Instantiation ist hier der Name des Spiels. Das Folgende ist eine etwas kompliziertere Art der Anwendung, die für alles andere als die einfachste Verwendung 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();
...
}
}
Während ich die Anwendungsunterklasse der Verwendung von Singletons als elegantere Lösung vorziehe, würde ich Entwickler lieber Singletons verwenden, wenn dies wirklich notwendig ist, als überhaupt nicht über die Leistung und die Multithreading-Auswirkungen der Zuordnung des Status zur Anwendungsunterklasse nachzudenken.
ANMERKUNG 1: Wie anticafe kommentiert hat, ist in der Manifestdatei ein Tag erforderlich, um Ihre Anwendungsüberschreibung korrekt mit Ihrer Anwendung zu verknüpfen. Weitere Informationen finden Sie in den Android-Dokumenten. Ein Beispiel:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
HINWEIS 2: user608578 fragt unten, wie dies mit der Verwaltung nativer Objektlebenszyklen funktioniert. Ich bin nicht im geringsten in der Lage, nativen Code mit Android zu verwenden, und ich bin nicht qualifiziert zu beantworten, wie dies mit meiner Lösung interagieren würde. Wenn jemand eine Antwort darauf hat, bin ich bereit, sie gutzuschreiben und die Informationen in diesen Beitrag zu setzen, um maximale Sichtbarkeit zu gewährleisten.
NACHTRAG:
Wie einige Leute bemerkt haben, ist dies keine Lösung für einen anhaltenden Zustand, was ich in der ursprünglichen Antwort vielleicht mehr hätte betonen sollen. Das heißt, dies ist keine Lösung zum Speichern von Benutzer- oder anderen Informationen, die über die gesamte Lebensdauer der Anwendung hinweg beibehalten werden sollen. Daher halte ich die meisten Kritikpunkte im Zusammenhang mit Anwendungen, die zu irgendeinem Zeitpunkt getötet werden, usw. für strittig, da alles, was jemals auf der Festplatte gespeichert werden musste, nicht über eine Anwendungsunterklasse gespeichert werden sollte. Es soll eine Lösung zum Speichern des temporären, leicht wiederherstellbaren Anwendungsstatus (unabhängig davon, ob ein Benutzer angemeldet ist) und von Komponenten sein, die eine einzelne Instanz (z. B. Application Network Manager) ( NICHT Singleton!) Sind.
Dayerman war so freundlich, auf ein interessantes Gespräch mit Reto Meier und Dianne Hackborn hinzuweisen, in dem von der Verwendung von Anwendungsunterklassen zugunsten von Singleton-Mustern abgeraten wird. Somatik hat auch schon früher auf etwas Ähnliches hingewiesen, obwohl ich es damals nicht gesehen habe. Aufgrund der Rolle von Reto und Dianne bei der Wartung der Android-Plattform kann ich nicht nach Treu und Glauben empfehlen, ihre Ratschläge zu ignorieren. Was sie sagen, geht. Ich möchte den Meinungen nicht zustimmen, die in Bezug auf die Bevorzugung von Singleton gegenüber Anwendungsunterklassen geäußert wurden. In meiner Meinungsverschiedenheit werde ich Konzepte verwenden, die am besten in dieser StackExchange-Erklärung des Singleton-Entwurfsmusters erläutert werden, 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, eine Unterklasse aus der Anwendung zu erstellen. Es ist nicht anders als ein Singleton zu erstellen ..." Diese erste Behauptung ist falsch. Dafür gibt es zwei Hauptgründe. 1) Die Anwendungsklasse bietet eine bessere lebenslange Garantie für einen Anwendungsentwickler. Es ist garantiert die Lebensdauer der Anwendung. Ein Singleton ist nicht AUSSCHLIESSLICH an die Lebensdauer der Anwendung gebunden (obwohl dies effektiv ist). Dies mag für einen durchschnittlichen Anwendungsentwickler kein Problem sein, aber ich würde behaupten, dass dies genau die Art von Vertrag ist, die die Android-API anbieten sollte, und es bietet auch dem Android-System viel mehr Flexibilität, indem die Lebensdauer der zugehörigen Anwendungen minimiert wird Daten. 2) Die Anwendungsklasse stellt dem Anwendungsentwickler einen einzelnen Instanzinhaber für den Status zur Verfügung. Das unterscheidet sich sehr von einem Singleton-Staatsinhaber. Eine Liste der Unterschiede finden Sie oben unter dem Link zur Singleton-Erklärung.
Dianne fährt fort: "... wahrscheinlich etwas, das Sie in Zukunft bereuen, wenn Sie feststellen, dass Ihr Anwendungsobjekt zu einem großen Durcheinander der unabhängigen Anwendungslogik wird." Dies ist sicherlich nicht falsch, aber dies ist kein Grund, Singleton der Anwendungsunterklasse vorzuziehen. Keines von Dianes Argumenten liefert einen Grund dafür, dass die Verwendung eines Singleton besser ist als eine Anwendungsunterklasse. Sie versucht lediglich festzustellen, dass die Verwendung eines Singleton nicht schlechter ist als eine Anwendungsunterklasse, was ich für falsch halte.
Sie fährt fort: "Und dies führt natürlicher dazu, wie Sie diese Dinge verwalten sollten - sie bei Bedarf zu initialisieren." Dies ignoriert die Tatsache, dass es keinen Grund gibt, warum Sie nicht bei Bedarf auch mit einer Anwendungsunterklasse initialisieren können. Auch hier gibt es keinen Unterschied.
Dianne endet mit "Das Framework selbst verfügt über Tonnen und Tonnen von Singletons für all die kleinen gemeinsam genutzten Daten, die es für die App verwaltet, wie z. B. Caches geladener Ressourcen, Pools von Objekten usw. Es funktioniert hervorragend." Ich behaupte nicht, dass die Verwendung von Singletons nicht gut funktioniert oder keine legitime Alternative darstellt. Ich argumentiere, dass Singletons keinen so starken Vertrag mit dem Android-System abschließen wie die Verwendung einer Anwendungsunterklasse, und dass die Verwendung von Singletons im Allgemeinen auf ein unflexibles Design hinweist, das nicht einfach zu ändern ist und später zu vielen Problemen führt. Meiner Meinung nach ist der starke Vertrag, den die Android-API für Entwickleranwendungen bietet, einer der attraktivsten und angenehmsten Aspekte der Programmierung mit Android und hat zu einer frühen Entwicklerakzeptanz geführt, die die Android-Plattform zu ihrem heutigen Erfolg geführt hat.
Dianne hat im Folgenden ebenfalls einen zusätzlichen Nachteil bei der Verwendung von Anwendungsunterklassen erwähnt, der möglicherweise dazu beiträgt oder das Schreiben von weniger Leistungscode erleichtert. Dies ist sehr richtig, und ich habe diese Antwort bearbeitet, um zu betonen, wie wichtig es ist, Perf hier zu berücksichtigen und den richtigen Ansatz zu wählen, wenn Sie Anwendungsunterklassen verwenden. Wie Dianne feststellt, ist es wichtig zu bedenken, dass Ihre Anwendungsklasse jedes Mal instanziiert wird, wenn Ihr Prozess geladen wird (kann mehrere Male gleichzeitig sein, wenn Ihre Anwendung in mehreren Prozessen ausgeführt wird!), Auch wenn der Prozess nur für eine Hintergrundsendung geladen wird Veranstaltung. Es ist daher wichtig, die Anwendungsklasse eher als Repository für Zeiger auf gemeinsam genutzte Komponenten Ihrer Anwendung zu verwenden, als als Ort für die Verarbeitung!
Ich überlasse Ihnen die folgende Liste der Nachteile von Singletons, die aus dem früheren StackExchange-Link gestohlen wurden:
- Unfähigkeit, abstrakte oder Schnittstellenklassen zu verwenden;
- Unfähigkeit zur Unterklasse;
- Hohe Kopplung über die Anwendung (schwer zu modifizieren);
- Schwer zu testen (kann in Unit-Tests nicht gefälscht / verspottet werden);
- Im veränderlichen Zustand schwer zu parallelisieren (erfordert umfangreiche Verriegelung);
und füge mein eigenes hinzu:
- Unklarer und nicht verwaltbarer lebenslanger Vertrag, der nicht für die Entwicklung von Android (oder den meisten anderen) geeignet ist;