Ich erstelle eine Anwendung, für die eine Anmeldung erforderlich ist. Ich habe die Haupt- und die Anmeldeaktivität erstellt.
In der Hauptaktivitätsmethode habe onCreate
ich die folgende Bedingung hinzugefügt:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
loadSettings();
if(strSessionString == null)
{
login();
}
...
}
Die onActivityResult
Methode, die ausgeführt wird, wenn das Anmeldeformular beendet wird, sieht folgendermaßen aus:
@Override
public void onActivityResult(int requestCode,
int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode)
{
case(SHOW_SUBACTICITY_LOGIN):
{
if(resultCode == Activity.RESULT_OK)
{
strSessionString = data.getStringExtra(Login.SESSIONSTRING);
connectionAvailable = true;
strUsername = data.getStringExtra(Login.USERNAME);
}
}
}
Das Problem ist, dass das Anmeldeformular manchmal zweimal angezeigt wird (die login()
Methode wird zweimal aufgerufen), und auch wenn die Telefontastatur verschoben wird, wird das Anmeldeformular erneut angezeigt, und ich denke, das Problem ist die Variable strSessionString
.
Weiß jemand, wie die Variable global festgelegt wird, um zu vermeiden, dass das Anmeldeformular angezeigt wird, nachdem sich der Benutzer bereits erfolgreich authentifiziert hat?
quelle
Antworten:
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 derContext.getApplicationContext()
Methode darauf zugreifen (Activity
bietet auch eine MethodegetApplication()
, die genau den gleichen Effekt hat). Das Folgende ist ein extrem vereinfachtes Beispiel mit folgenden Einschränkungen: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:
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:
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:
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:
und füge mein eigenes hinzu:
quelle
Erstellen Sie diese Unterklasse
Fügen Sie in der Datei AndroidManifest.xml android: name hinzu
Beispiel
quelle
java.lang.IllegalAccessException: access to class is not allowed
Die von Soonil vorgeschlagene Methode, einen Status für die Anwendung beizubehalten, ist gut, hat jedoch eine Schwachstelle: Es gibt Fälle, in denen das Betriebssystem den gesamten Anwendungsprozess abbricht. Hier ist die Dokumentation dazu - Prozesse und Lebenszyklen .
Stellen Sie sich einen Fall vor - Ihre App tritt in den Hintergrund, weil Sie jemand anruft (die Telefon-App steht jetzt im Vordergrund). In diesem Fall && kann das Betriebssystem unter bestimmten Bedingungen (überprüfen Sie den obigen Link auf mögliche Fehler) Ihren Anwendungsprozess, einschließlich der
Application
Unterklasseninstanz, beenden. Infolgedessen geht der Zustand verloren. Wenn Sie später zur Anwendung zurückkehren, stellt das Betriebssystem seinen Aktivitätsstapel und seineApplication
Unterklasseninstanz wieder her, dasmyState
Feld jedochnull
.AFAIK, die einzige Möglichkeit, die Sicherheit des Staates zu gewährleisten, besteht darin, eine Art von Persistenz des Staates zu verwenden, z. B. eine private für die Anwendungsdatei oder
SharedPrefernces
(schließlich eine private für die Anwendungsdatei im internen Dateisystem).quelle
SharedPreferences
; So habe ich es gesehen. Ich finde es seltsam, das Präferenzsystem für den gespeicherten Zustand zu missbrauchen, aber es funktioniert so gut, dass das Problem nur eine Frage der Terminologie wird.Nur eine Notiz ..
hinzufügen:
oder wie auch immer Sie Ihre Unterklasse für das vorhandene
<application>
Tag benannt haben. Ich habe immer wieder versucht,<application>
dem Manifest ein weiteres Tag hinzuzufügen , und habe eine Ausnahme erhalten.quelle
Ich konnte auch nicht finden, wie das Anwendungs-Tag angegeben werden soll, aber nach vielem Googeln wurde aus den Dokumenten der Manifestdatei ersichtlich: Verwenden Sie android: name zusätzlich zum Standardsymbol und der Bezeichnung in der Zeilengruppe der Anwendung.
android: name Der vollständig qualifizierte Name einer für die Anwendung implementierten Anwendungsunterklasse. Wenn der Anwendungsprozess gestartet wird, wird diese Klasse vor einer der Komponenten der Anwendung instanziiert.
Die Unterklasse ist optional. Die meisten Anwendungen benötigen keine. Wenn keine Unterklasse vorhanden ist, verwendet Android eine Instanz der Basisanwendungsklasse.
quelle
Was ist mit der Sicherstellung der Sammlung von nativem Speicher mit solchen globalen Strukturen?
Aktivitäten haben eine
onPause/onDestroy()
Methode, die als Zerstörung bezeichnet wird, aber die Anwendungsklasse hat keine Entsprechungen. Welcher Mechanismus wird empfohlen, um sicherzustellen, dass globale Strukturen (insbesondere solche, die Verweise auf nativen Speicher enthalten) ordnungsgemäß gesammelt werden, wenn die Anwendung entweder beendet oder der Taskstapel in den Hintergrund gestellt wird?quelle
Sie müssen lediglich einen Anwendungsnamen wie den folgenden definieren, der funktioniert:
quelle
Wie oben erläutert, konnte das Betriebssystem die ANWENDUNG ohne Benachrichtigung beenden (es gibt kein onDestroy-Ereignis), sodass diese globalen Variablen nicht gespeichert werden können.
SharedPreferences könnte eine Lösung sein, AUSSER Sie haben COMPLEX STRUCTURED-Variablen (in meinem Fall hatte ich ein Integer-Array zum Speichern der IDs, die der Benutzer bereits verarbeitet hat). Das Problem mit den SharedPreferences besteht darin, dass es schwierig ist, diese Strukturen jedes Mal zu speichern und abzurufen, wenn die erforderlichen Werte benötigt werden.
In meinem Fall hatte ich einen Hintergrund-SERVICE, damit ich diese Variablen dorthin verschieben konnte. Da der Service das Ereignis onDestroy hat, konnte ich diese Werte einfach speichern.
quelle
Wenn einige Variablen in SQLite gespeichert sind und Sie sie in den meisten Aktivitäten Ihrer App verwenden müssen. dann Anwendung vielleicht der beste Weg, um es zu erreichen. Fragen Sie die Variablen beim Start der Anwendung aus der Datenbank ab und speichern Sie sie in einem Feld. Dann können Sie diese Variablen in Ihren Aktivitäten verwenden.
Finden Sie also den richtigen Weg, und es gibt keinen besten Weg.
quelle
Sie können ein statisches Feld haben, um diese Art von Status zu speichern. Oder legen Sie es in das Ressourcen-Bundle und stellen Sie es von dort auf onCreate (Bundle savedInstanceState) wieder her. Stellen Sie einfach sicher, dass Sie den von der Android-App verwalteten Lebenszyklus vollständig verstehen (z. B. warum login () bei einer Änderung der Tastaturausrichtung aufgerufen wird).
quelle
Verwenden Sie kein anderes
<application>
Tag in der Manifestdatei. Nehmen Sie nur eine Änderung am vorhandenen<application>
Tag vor und fügen Sie diese Zeile hinzuandroid:name=".ApplicationName"
,ApplicationName
in der der Name Ihrer Unterklasse (zum Speichern des globalen Tags ) angegeben ist, die Sie erstellen möchten .Schließlich sollte Ihr EINZIGES
<application>
Tag in der Manifestdatei folgendermaßen aussehen:quelle
Sie können Intents, Sqlite oder Shared Preferences verwenden. Wenn es um den Medienspeicher geht, wie Dokumente, Fotos und Videos, können Sie stattdessen die neuen Dateien erstellen.
quelle
Sie können dies mit zwei Ansätzen tun:
Gemeinsame Einstellungen verwenden
Anwendungsklasse verwenden
Beispiel:
Sie können die obige Klasse verwenden, um die Anmeldung in Ihrer MainActivity wie folgt zu implementieren. Code sieht ungefähr so aus:
Diese Methode funktioniert für die temporäre Speicherung. Sie wissen wirklich nicht, wann das Betriebssystem die Anwendung wegen des geringen Arbeitsspeichers beenden wird. Wenn sich Ihre Anwendung im Hintergrund befindet und der Benutzer durch eine andere Anwendung navigiert, für deren Ausführung mehr Speicher erforderlich ist, wird Ihre Anwendung beendet, da das Betriebssystem Vordergrundprozessen mehr Priorität einräumt als dem Hintergrund. Daher ist Ihr Anwendungsobjekt null, bevor sich der Benutzer abmeldet. Daher empfehle ich, die oben angegebene zweite Methode zu verwenden.
Gemeinsame Einstellungen verwenden.
quelle
Das Aktivitätsergebnis wird zuvor beim Fortsetzen aufgerufen. Verschieben Sie also Ihre Anmeldeprüfung auf "Wiederaufnahme" und Ihre zweite Anmeldung kann blockiert werden, sobald die zweite Aktivität ein positives Ergebnis zurückgegeben hat. On Resume wird jedes Mal aufgerufen, sodass Sie sich keine Sorgen machen müssen, dass es nicht beim ersten Mal aufgerufen wird.
quelle
Der Ansatz der Unterklasse wurde auch vom BARACUS-Framework verwendet. Aus meiner Sicht sollte die Unterklasse Application mit den Lebenszyklen von Android arbeiten. Dies ist, was jeder Anwendungscontainer tut. Anstatt dann Globals zu haben, registriere ich Beans in diesem Kontext und lasse sie in jede Klasse injizieren, die durch den Kontext verwaltet werden kann. Jede injizierte Bean-Instanz ist tatsächlich ein Singleton.
Einzelheiten finden Sie in diesem Beispiel
Warum manuell arbeiten, wenn Sie so viel mehr haben können?
quelle
quelle
Sie können eine Klasse erstellen, die die Klasse erweitert
Application
, Ihre Variable dann als Feld dieser Klasse deklarieren und eine Getter-Methode dafür bereitstellen.Verwenden Sie dann Folgendes, um auf diese Variable in Ihrer Aktivität zuzugreifen:
quelle