Frage

Ich erstelle eine Anwendung, die eine Anmeldung erfordert.Ich habe die Haupt- und die Anmeldeaktivität erstellt.

In der Haupttätigkeit onCreate Methode habe ich die folgende Bedingung hinzugefügt:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ...

    loadSettings();
    if(strSessionString == null)
    {
        login();
    }
    ...
}

Der 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 besteht darin, dass das Anmeldeformular manchmal zweimal erscheint (das login() Methode wird zweimal aufgerufen) und auch wenn die Telefontastatur verschiebt, erscheint das Anmeldeformular erneut und ich vermute, dass das Problem an der Variablen liegt strSessionString.

Weiß jemand, wie man die Variable global setzt, um zu vermeiden, dass das Anmeldeformular angezeigt wird, nachdem sich der Benutzer bereits erfolgreich authentifiziert hat?

War es hilfreich?

Lösung

Ich habe diese Antwort im Jahr 2009 geschrieben, als Android noch relativ neu war und es viele nicht gut etablierte Bereiche in der Android-Entwicklung gab.Ich habe am Ende dieses Beitrags einen langen Nachtrag hinzugefügt, in dem ich einige Kritikpunkte anspreche und eine philosophische Meinungsverschiedenheit beschreibe, die ich bezüglich der Verwendung von Singletons anstelle der Unterklassenanwendung habe.Lesen Sie es auf eigene Gefahr.

URSPRÜNGLICHE ANTWORT:

Das allgemeinere Problem, auf das Sie stoßen, besteht darin, den Status über mehrere Aktivitäten und alle Teile Ihrer Anwendung hinweg zu speichern.Eine statische Variable (z. B. ein Singleton) ist eine gängige Java-Methode, um dies zu erreichen.Ich habe jedoch festgestellt, dass es in Android eine elegantere Möglichkeit ist, Ihren Status mit dem Anwendungskontext zu verknüpfen.

Wie Sie wissen, ist jede Aktivität auch ein Kontext, also Informationen über ihre Ausführungsumgebung im weitesten Sinne.Ihre Anwendung verfügt auch über einen Kontext, und Android garantiert, dass sie in Ihrer gesamten Anwendung als einzelne Instanz vorhanden ist.

Der Weg dazu besteht darin, eine eigene Unterklasse von zu erstellen android.app.Application, und geben Sie dann 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 überall darauf zugreifen context Verwendung der Context.getApplicationContext() Methode (Activity stellt auch eine Methode bereit getApplication() was genau den gleichen Effekt hat).Im Folgenden finden Sie ein stark 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 bestehende Android-Framework integrieren.Beachten Sie, dass dies nicht prozessübergreifend funktioniert (sollte Ihre App eine der seltenen sein, die über mehrere Prozesse verfügt).

Etwas, das Sie aus dem obigen Beispiel beachten sollten;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 (z. B. Zugriff auf die Festplatte, Zugriff auf das Netzwerk, irgendetwas Blockierendes usw.) jedes Mal durchgeführt, wenn die Anwendung instanziiert wird!Sie denken vielleicht, dass dies nur einmal für den Vorgang ist und ich die Kosten sowieso bezahlen muss, oder?Wie Dianne Hackborn weiter unten erwähnt, ist es beispielsweise durchaus möglich, dass Ihr Prozess instanziiert wird – nur – um ein Hintergrundübertragungsereignis 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 das A und O.Das Folgende ist eine etwas kompliziertere Art der Verwendung von Application, 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();
    ...
  }
}

Obwohl ich die Unterklasse von Anwendungen der Verwendung von Singletons hier als elegantere Lösung vorziehe, wäre es mir lieber, wenn Entwickler bei Bedarf Singletons verwenden, anstatt überhaupt über die Auswirkungen auf die Leistung und das Multithreading nachzudenken, die sich aus der Verknüpfung des Status mit der Unterklasse der Anwendung ergeben.

ANMERKUNG 1: Wie auch Anticafe kommentierte, ist ein Tag in der Manifestdatei erforderlich, um Ihre Anwendungsüberschreibung korrekt mit Ihrer Anwendung zu verknüpfen.Weitere Informationen finden Sie auch hier in den Android-Dokumenten.Ein Beispiel:

<application
     android:name="my.application.MyApp" 
     android:icon="..."
     android:label="...">
</application>

ANMERKUNG 2: user608578 fragt im Folgenden, wie dies mit der Verwaltung nativer Objektlebenszyklen funktioniert.Ich bin nicht im Geringsten mit der Verwendung von nativem Code mit Android vertraut und kann nicht beantworten, wie das mit meiner Lösung interagieren würde.Wenn jemand darauf eine Antwort hat, bin ich bereit, dies zu erwähnen und die Informationen für maximale Sichtbarkeit in diesen Beitrag zu stellen.

NACHTRAG:

Wie einige Leute bemerkt haben, ist dies der Fall nicht eine Lösung für hartnäckig Zustand, etwas, das ich in der ursprünglichen Antwort vielleicht mehr hätte betonen sollen.D.h.Dies ist nicht als Lösung zum Speichern von Benutzer- oder anderen Informationen gedacht, die über die gesamte Anwendungslebensdauer hinweg beibehalten werden sollen.Daher halte ich die meisten Kritikpunkte im Folgenden bezüglich des jederzeitigen Beendens von Anwendungen usw. für irrelevant, da alles, was jemals auf der Festplatte gespeichert werden musste, nicht über eine Anwendungsunterklasse gespeichert werden sollte.Es soll eine Lösung zum Speichern temporärer, leicht wiederherstellbarer Anwendungszustände (z. B. ob ein Benutzer angemeldet ist) und einzelner Komponenten (z. B. Anwendungsnetzwerkmanager) sein (NICHT Singleton!) in der Natur.

Dayerman war so freundlich, auf etwas Interessantes hinzuweisen Gespräch mit Reto Meier und Dianne Hackborn In diesem Fall wird von der Verwendung von Anwendungsunterklassen zugunsten von Singleton-Mustern abgeraten.Auch Somatik hat früher auf etwas in dieser Art hingewiesen, obwohl ich es damals noch nicht gesehen habe.Aufgrund der Rolle von Reto und Dianne bei der Wartung der Android-Plattform kann ich nicht guten Gewissens empfehlen, ihren Rat zu ignorieren.Was sie sagen, gilt.Ich möchte den geäußerten Meinungen hinsichtlich der Bevorzugung von Singleton gegenüber Application-Unterklassen nicht zustimmen.In meiner Meinungsverschiedenheit werde ich die Konzepte verwenden, die am besten in erklärt werden diese StackExchange-Erklärung des Singleton-Entwurfsmusters, sodass 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 von Application zu erstellen.“Es ist nicht anders, als einen Singleton zu erstellen …“ Diese erste Behauptung ist falsch.Dafür gibt es zwei Hauptgründe.1) Die Application-Klasse bietet eine bessere lebenslange Garantie für einen Anwendungsentwickler;Die Lebensdauer der Anwendung ist garantiert.Ein Singleton ist nicht AUSDRÜCKLICH an die Lebensdauer der Anwendung gebunden (obwohl dies effektiv der Fall ist).Für einen durchschnittlichen Anwendungsentwickler stellt das vielleicht kein Problem dar, aber ich würde behaupten, dass dies genau die Art von Vertrag ist, die die Android-API bieten sollte, und sie bietet auch viel mehr Flexibilität für das Android-System, indem sie die Lebensdauer der zugehörigen Verträge minimiert Daten.2) Die Application-Klasse stellt dem Anwendungsentwickler einen einzelnen Instanzhalter für den Status zur Verfügung, der sich stark von einem Singleton-Inhaber für den Status unterscheidet.Eine Liste der Unterschiede finden Sie oben im Link zur Singleton-Erklärung.

Dianne fährt fort, "... wahrscheinlich etwas zu sein, das Sie in Zukunft bedauern, da Sie feststellen, dass Ihr Anwendungsobjekt zu diesem großen Durcheinander der unabhängigen Anwendungslogik wird." Dies ist sicherlich nicht falsch, aber dies ist kein Grund für die Auswahl von Singleton über die Anwendungsunterklasse.Keines von Dianes Argumenten liefert einen Grund dafür, dass die Verwendung eines Singletons besser ist als eine Application-Unterklasse. Sie versucht lediglich nachzuweisen, dass die Verwendung eines Singletons nicht schlechter ist als eine Application-Unterklasse, was meiner Meinung nach falsch ist.

Sie fährt fort: "Und das führt natürlicher dazu, wie Sie diese Dinge verwalten sollten - sie auf Nachfrage initialisieren." Dies ignoriert die Tatsache, dass es keinen Grund gibt, warum Sie auch nicht mithilfe einer Anwendungsunterklasse bei Bedarf initialisieren können.Auch hier gibt es keinen Unterschied.

Dianne endet mit „Das Framework selbst verfügt über Unmengen von Singletons für all die kleinen gemeinsamen Daten, die es für die App verwaltet, wie etwa Caches geladener Ressourcen, Pools von Objekten usw.“Es funktioniert großartig.“ Ich behaupte nicht, dass die Verwendung von Singletons nicht gut funktionieren kann oder keine legitime Alternative darstellt.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 geändert werden kann und später zu vielen Problemen führt.Meiner Meinung nach ist der starke Vertrag, den die Android-API Entwickleranwendungen bietet, einer der attraktivsten und erfreulichsten Aspekte der Programmierung mit Android und hat zu einer frühen Akzeptanz durch Entwickler geführt, die der Android-Plattform zu dem Erfolg verholfen hat, den sie heute 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 unten ebenfalls einen Kommentar abgegeben und einen weiteren Nachteil der Verwendung von Anwendungsunterklassen erwähnt: Sie können das Schreiben von Code mit geringerer Leistung fördern oder einfacher machen.Das ist sehr wahr, und ich habe diese Antwort bearbeitet, um hervorzuheben, wie wichtig es ist, hier Perf zu berücksichtigen und den richtigen Ansatz zu wählen, wenn Sie Anwendungsunterklassen verwenden.Wie Dianne feststellt, ist es wichtig, sich daran zu erinnern, dass Ihre Anwendungsklasse jedes Mal instanziiert wird, wenn Ihr Prozess geladen wird (möglicherweise mehrmals gleichzeitig, wenn Ihre Anwendung in mehreren Prozessen ausgeführt wird!), auch wenn der Prozess nur für eine Hintergrundübertragung geladen wird Ereignis.Daher ist es wichtig, die Application-Klasse eher als Repository für Zeiger auf gemeinsam genutzte Komponenten Ihrer Anwendung zu verwenden und nicht als Ort für die Verarbeitung!

Ich überlasse Ihnen die folgende Liste der Nachteile von Singletons, die dem früheren StackExchange-Link entnommen wurden:

  • Unfähigkeit, abstrakte Klassen oder Schnittstellenklassen zu verwenden;
  • Unfähigkeit zur Unterklassifizierung;
  • Hohe Kopplung über die gesamte Anwendung hinweg (schwer zu ändern);
  • Schwierig zu testen (kann in Unit-Tests nicht gefälscht/verspottet werden);
  • Bei veränderlichem Zustand schwierig zu parallelisieren (erfordert umfassende Sperrung);

und füge mein eigenes hinzu:

  • Unklarer und unüberschaubarer lebenslanger Vertrag, der für die Android-Entwicklung (oder die meisten anderen) ungeeignet ist;

Andere Tipps

Erstellen Sie diese Unterklasse

public class MyApp extends Application {
  String foo;
}

Im AndroidManifest.xml hinzufügen android: name

Beispiel:

<application android:name=".MyApp" 
       android:icon="@drawable/icon" 
       android:label="@string/app_name">

Die vorgeschlagene durch Soonil Weise einen Zustand für die Anwendung zu halten ist gut, aber es hat einen Schwachpunkt - es gibt Fälle, wenn OS den gesamten Bewerbungsprozess tötet. Hier ist die Dokumentation zu diesem Thema -. Prozesse und Lifecycles

sei ein Fall betrachtet - Ihre App in den Hintergrund geht, weil jemand Sie anruft (Telefon App im Vordergrund steht jetzt). In diesem Fall && unter einigen anderen Bedingungen (überprüfen Sie den Link oben für das, was sie sein könnten) die OS Ihre Bewerbungsverfahren töten können, einschließlich der Application Unterklasse-Instanz. Als Ergebnis wird der Zustand verloren. Wenn Sie später an die Anwendung zurück, dann die OS seine Aktivität Stapel wieder herstellen und Application Unterklasse Instanz jedoch das myState Feld null wird.

AFAIK, der einzige Weg, Staatssicherheit zu gewährleisten, ist jede Art zu verwenden, um den Zustand des persistierenden, z.B. mit einem privaten für die Anwendungsdatei oder SharedPrefernces (es verwendet schließlich ein private für die Anwendungsdatei im internen Dateisystem).

Nur ein Hinweis ..

add:

android:name=".Globals"

oder was auch immer Sie Namen der Unterklasse auf den vorhandenen <application>-Tag. Ich versuche immer wieder einen anderen <application> Tag zu dem Manifest hinzuzufügen und würde eine Exception.

Ich kann nicht finden, wie die Anwendung Tag entweder angeben, aber nach vielen googeln, wurde es von der Manifest-Datei docs die Hand: Verwendung android: name, zusätzlich zu dem Standard-Icon und Label in der Anwendung Strophe.

android: name Der vollständig qualifizierte Name eines für die Anwendung implementiert Anwendungsunterklassen. Wenn der Anwendungsprozess gestartet wird, wird diese Klasse instanziiert, bevor eine der Komponenten der Anwendung.

Die Unterklasse ist optional; die meisten Anwendungen nicht brauchen. In Abwesenheit einer Unterklasse, verwendet Android eine Instanz der Basisanwendungsklasse.

Was ist die Sammlung von nativen Speicher mit einer solchen globalen Strukturen gewährleistet werden?

Aktivitäten haben eine onPause/onDestroy() Methode, die auf Zerstörung genannt, aber die Application-Klasse hat keine Entsprechungen. Welcher Mechanismus wird empfohlen, dass die globalen Strukturen zu gewährleisten (insbesondere solche, die Verweise auf nativen Speicher) ist Müll gesammelt angemessen, wenn die Anwendung entweder getötet oder der Task-Stack in dem Hintergrund?

Nur müssen Sie einen Anwendungsnamen wie unten definieren, die funktionieren:

<application
  android:name="ApplicationName" android:icon="@drawable/icon">
</application>

Wie es oben O diskutiert wurde die Anwendung ohne Benachrichtigung töten konnte (es gibt kein onDestroy Ereignis), so gibt es keine Möglichkeit, diese globalen Variablen zu speichern.

SharedPreferences könnte eine Lösung sein AUSSER Sie komplexe strukturierte Variablen (in meinem Fall hatte ich Integer-Array die IDs zu speichern, die der Benutzer bereits behandelt hat). Das Problem mit den SharedPreferences ist, dass es schwierig ist, diese Strukturen jedes Mal, wenn die benötigten Werte zu speichern und abzurufen.

In meinem Fall habe ich einen Hintergrunddienst hatte, so konnte ich diese Variablen dort bewegen und, da der Dienst onDestroy Ereignis hat, konnte ich leicht, diese Werte speichern.

Wenn einige Variablen in SQLite gespeichert und Sie müssen sie in den meisten Aktivitäten in der App verwenden. dann Anwendung vielleicht der beste Weg, es zu erreichen. Abfrage der Variablen aus der Datenbank, wenn die Anwendung gestartet und speichern sie in einem Feld. Dann können Sie diese Variablen in Ihre Aktivitäten nutzen.

So, den richtigen Weg finden, und es gibt keinen besten Weg.

können Sie haben ein statisches Feld, diese Art von Zustand zu speichern. Oder setzen sie auf die Ressource Bundle und Wiederherstellen von dort auf onCreate (Bundle savedInstanceState). So stellen Sie sicher, dass Sie völlig Android App Managed Lifecycle verstehen (z warum login () auf der Tastatur Orientierungsänderung aufgerufen wird).

NICHT Verwenden Sie einen anderen <application> Tag in manifest file.Just Sie eine Änderung in der bestehenden <application> Tag, diese Zeile android:name=".ApplicationName" hinzufügen, wo ApplicationName Namen Ihrer Unterklasse sein wird (Verwendung speichern global) dass Sie ist zu erstellen.

so, schließlich Ihr ONE AND ONLY <application> Tag in Manifest-Datei soll wie folgt aussehen: -

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.NoActionBar"
        android:name=".ApplicationName"
        >

Sie können Intents, SQLite oder Gemeinschafts-Einstellungen verwenden. Wenn es um den Medienspeicher kommt, wie Dokumente, Fotos und Videos, können Sie die neuen Dateien stattdessen erstellen.

Sie können dies auf zwei verschiedenen Wegen tun:

  1. Verwenden von Anwendungsklasse
  2. Mit Gemeinschafts-Einstellungen

  3. Mit Application-Klasse

Beispiel:

class SessionManager extends Application{

  String sessionKey;

  setSessionKey(String key){
    this.sessionKey=key;
  }

  String getSessisonKey(){
    return this.sessionKey;
  }
}

Sie können die oben Klasse verwenden Login in Ihrem MainActivity wie unten zu implementieren. Code wird wie folgt aussehen:

@override 
public void onCreate (Bundle savedInstanceState){
  // you will this key when first time login is successful.
  SessionManager session= (SessionManager)getApplicationContext();
  String key=getSessisonKey.getKey();
  //Use this key to identify whether session is alive or not.
}

Diese Methode wird für die temporäre Speicherung arbeiten. Sie tun wirklich keine Ahnung, wenn das System in Betrieb ist im Begriff, die Anwendung zu töten, wegen der niedrigen Speicher. gegeben mehr Priorität Vordergrund Prozesse als Hintergrund, wenn Ihre Anwendung im Hintergrund und Benutzer wird durch andere Anwendung navigiert, die mehr Speicher zu laufen verlangt, dann wird Ihre Anwendung, da Betriebssystem getötet werden. Daher Ihr Anwendungsobjekt ist null, bevor Benutzer abmeldet. Daher dafür empfehle ich oben angegebene zweite Methode zu verwenden.

  1. shared Einstellungen benutzen.

    String MYPREF="com.your.application.session"
    
    SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE);
    
    //Insert key as below:
    
    Editot editor= pref.edit();
    
    editor.putString("key","value");
    
    editor.commit();
    
    //Get key as below.
    
    SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
    
    String key= getResources().getString("key");
    

Auf Aktivität Ergebnis wird, bevor sie auf Lebenslauf genannt. So bewegen Sie sich auf Lebenslauf überprüfen einloggen und Ihre zweite Login gesperrt werden, sobald die secomd Aktivität ein positives Ergebnis zurückgegeben hat. Auf Lebenslauf wird jedes Mal aufgerufen, so ist es nicht Sorgen der es nicht das erste Mal aufgerufen wird.

Der Ansatz von Subklassifizieren wird auch von dem baracus Rahmen verwendet. Aus meiner Sicht Subklassifizieren Anwendung bestimmt war mit den Lebenszyklen von Android zu arbeiten; das ist, was jede Application Container tut. Statt dann Globals hat, melde ich mich Bohnen zu diesem Zusammenhang eine ließ sie in jeder Klasse überschaubar durch den Kontext injiziert beeing. Jede injiziert Bean-Instanz ist eigentlich ein Singleton.

dieses Beispiel Siehe Details

Warum tun manuelle Arbeit, wenn Sie so viel mehr haben kann?

class GlobaleVariableDemo extends Application {

    private String myGlobalState;

    public String getGlobalState(){
     return myGlobalState;
    }
    public void setGlobalState(String s){
     myGlobalState = s;
    }
}

class Demo extends Activity {

@Override
public void onCreate(Bundle b){
    ...
    GlobaleVariableDemo appState = ((GlobaleVariableDemo)getApplicationContext());
    String state = appState.getGlobalState();
    ...
    }
}

Sie können eine Klasse erstellen, die Application-Klasse erweitert und dann erklären, um Ihre Variable als Feld dieser Klasse und Getter-Methode für sie bereitstellt.

public class MyApplication extends Application {
    private String str = "My String";

    synchronized public String getMyString {
        return str;
    }
}

Und dann diese Variable in Ihrer Aktivität zuzugreifen, verwenden Sie diese:

MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top