608 Stimmen

Wie deklariert man globale Variablen in Android?

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();
    }
    ...
}

El onActivityResult Methode, die beim Beenden des Anmeldeformulars ausgeführt wird, sieht wie folgt 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 erscheint (die login() Methode wird zweimal aufgerufen) und auch wenn die Telefontastatur gleitet, erscheint das Anmeldeformular wieder und ich vermute, das Problem ist die Variable strSessionString .

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

967voto

sooniln Punkte 14492

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;

155voto

Guillaume Punkte 1671

Erstellen Sie diese Unterklasse

public class MyApp extends Application {
  String foo;
}

Fügen Sie in der Datei AndroidManifest.xml Android:name

Beispiel

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

0 Stimmen

java.lang.IllegalAccessException: access to class is not allowed

142voto

Vit Khudenko Punkte 27989

Der von Soonil vorgeschlagene Weg, einen Status für die Anwendung aufrechtzuerhalten, ist gut, hat aber einen Schwachpunkt - es gibt Fälle, in denen das Betriebssystem den gesamten Anwendungsprozess abbricht. Hier ist die Dokumentation zu diesem Thema - Prozesse und Lebenszyklen .

Nehmen wir den Fall an, dass Ihre App in den Hintergrund geht, weil Sie jemand anruft (die Telefon-App ist jetzt im Vordergrund). In diesem Fall && unter einigen anderen Bedingungen (siehe den obigen Link) kann das Betriebssystem Ihren Anwendungsprozess beenden, einschließlich der Application Unterklassen-Instanz. Infolgedessen ist der Zustand verloren. Wenn Sie später zur Anwendung zurückkehren, wird das Betriebssystem seinen Aktivitätsstapel wiederherstellen und Application Unterklasseninstanz, aber die myState wird das Feld null .

AFAIK ist die einzige Möglichkeit, die Sicherheit des Zustands zu garantieren, die Verwendung irgendeiner Art von Persistenz des Zustands, z. B. mit einer privaten für die Anwendungsdatei oder SharedPrefernces (er verwendet eventuell eine private Datei für die Anwendungsdatei im internen Dateisystem).

25voto

Gimbl Punkte 1460

Nur eine Anmerkung ..

hinzufügen:

android:name=".Globals"

oder wie auch immer Sie Ihre Unterklasse nennen, in die bestehende <application> Tag. Ich habe immer wieder versucht, ein weiteres <application> Tag in das Manifest einfügen und würde eine Ausnahme erhalten.

13voto

user608578 Punkte 201

Wie lässt sich die Erfassung des Eigenspeichers mit solchen globalen Strukturen sicherstellen?

Aktivitäten haben einen onPause/onDestroy() Methode, die bei der Zerstörung aufgerufen wird, aber die Klasse Application hat keine Entsprechungen. Welche Mechanismen werden empfohlen, um sicherzustellen, dass globale Strukturen (insbesondere solche, die Verweise auf systemeigenen Speicher enthalten) ordnungsgemäß gelöscht werden, wenn die Anwendung entweder beendet wird oder der Aufgabenstapel in den Hintergrund gestellt wird?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X