Eine Lanze für android.app.Application brechen
In der Dokumentation von android.app.Application heißt es:
There is normally no need to subclass Application. In most situation, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a
Context
which internally usesContext.getApplicationContext()
when first constructing the singleton.
Aber stimmt das so? Oder gibt es doch Fälle in denen es sinnvoll wäre android.app.Application zu nutzen?
Der zweite Teil ist schon korrekt. Ich habe nichts gegen Singletons an sich. Und sie zu nutzen um die Modularisierung der Applikation zu erhöhen ist bestimmt eine gute Idee.
Die Probleme
Aber Singletons haben ein Problem: Sie müssen initialisiert werden. Und das kann recht kniffelig werden.
Der übliche Weg der späten Initialisierung hat recht große Nebenläufigkeitsprobleme. Zu diesem Thema gibt es ausreichend Informationen und Vorschläge, wie man sie teilweise mit komplizierten Techniken wie Doppel-Locking bewältigen kann.
Zudem braucht die getInstance ()-Methode mit großer Wahrscheinlichkeit einen android.content.Context als Parameter. Was gleichzeitig den Aufruf kompliziert und Fehleranfällig macht. Es bedarf nur eines Programmierers im Team, der eine android.app.Activity (aka this) für das getInstance () nutzt und schon hat man sowohl ein Speicherloch als auch einen schwer zu findenden sporadischen Absturz.
Zur Erinnerung: Selbst wenn die App nur eine einzige android.app.Activity hat — sie wird zerstört und neu erstellt wenn das Gerät gedreht wird. Man kann zwar das Neu-Erstellen verhindern, aber dies macht den gesamten Rotationsablauf komplizierter und fehleranfälliger. Man sollte dies wirklich nur dann machen wenn es keine anderer Alternative gibt.
Alternativ könnte man die Singletons im onCreate der Hauptaktivität erstellen. Damit löst man die Probleme der Nebenläufigkeit und verbessert die Lesbarkeit des Codes. Auch kann man durch Verwendung separater createInstance ()-Methoden um die Verwendung eines context-Parameters bei getInstance ()-Methoden herumkommen. Allerdings hat auch dieses Vorgehen ein Problem: Es es gibt viele Fälle bei dem gar keine Activity erstellt wird: android.content.BroadcastReceiver, android.app.Service oder Instrumentation-Tests. Besonders Tests sind ein Problem: Instrumentation-Tests können alle Instanzen außer der Reihe erstellen
Initialisieren in allen Activities und Receivern? Klinkt kompliziert und wiederum Fehleranfällig.
Die Lösung
Aber wir müssen es uns gar nicht so schwer machen. Es gibt ja android.app.Application.onCreate (). Diese Methode wird garantiert nur einmal vor allen anderen Funktionen aufgerufen, dadurch können Probleme mit der Nebenläufigkeit nicht mehr auftauchen – ein if (!initialised) ist nicht mehr nötig. Alle Initialisierungen werden in der korrekten Reihenfolge durchgeführt und this ist ein context, der garantiert so lange gültig ist wie der Prozess läuft.
Um Missverständnisse von vorn herein auszuräumen: Ich sage nicht, dass man android.app.Application verwenden soll um die Singletons zu ersetzten. Das würde zum God-Object Anti-Pattern führen. Die zu initialisierenden Daten sollten auch weiterhin separat gehalten werden.
Zum guten Schluss kann man, wenn nötig, für die Instrumentation-Tests eine MockApplication-Klasse erstellen, welche die Singletons mit Mock-Objekten befüllt.