android-activity erklärung - Speichern des Status der Android-Aktivität mithilfe des Status der Instanz speichern




für context (23)

Ich habe an einer Android SDK-Plattform gearbeitet, und es ist ein wenig unklar, wie der Status einer Anwendung gespeichert wird. In Anbetracht dieser kleinen Umrüstung des Beispiels "Hallo, Android":

package com.android.hello;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloAndroid extends Activity {

  private TextView mTextView = null;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    if (savedInstanceState == null) {
       mTextView.setText("Welcome to HelloAndroid!");
    } else {
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}

Ich dachte, es wäre für den einfachsten Fall ausreichend, aber es antwortet immer mit der ersten Nachricht, egal wie ich von der App weg navigiere.

Ich bin mir sicher, dass die Lösung so einfach ist, wie onPause oder ähnliches zu überschreiben, aber ich habe mich seit onPause 30 Minuten in der Dokumentation onPause und nichts offensichtliches gefunden.


Answers

Wenn eine Aktivität erstellt wird, wird die onCreate () -Methode aufgerufen.

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

savedInstanceState ist ein Objekt der Bundle-Klasse, das zum ersten Mal null ist, aber Werte enthält, wenn es neu erstellt wird. Um den Status der Aktivität zu speichern, müssen Sie onSaveInstanceState () überschreiben.

   @Override
    protected void onSaveInstanceState(Bundle outState) {
      outState.putString("key","Welcome Back")
        super.onSaveInstanceState(outState);       //save state
    }

Setzen Sie Ihre Werte in "outState" -Objekt wie "outState.putString" ("key", "Welcome Back") und speichern Sie sie, indem Sie super aufrufen. Wenn die Aktivität zerstört wird, wird der Status im Bundle-Objekt gespeichert und kann nach der Wiederherstellung in onCreate () oder onRestoreInstanceState () wiederhergestellt werden. Bündel, die in onCreate () und onRestoreInstanceState () empfangen wurden, sind identisch.

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

          //restore activity's state
         if(savedInstanceState!=null){
          String reStoredString=savedInstanceState.getString("key");
            }
    }

oder

  //restores activity's saved state
 @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
      String restoredMessage=savedInstanceState.getString("key");
    }

Hier ist ein Kommentar von Steve Moseleys Antwort (von ToolmakerSteve ), der die Dinge in die richtige Perspektive bringt (im ganzen onSaveInstanceState vs. onPause, Ostkosten vs. Westkostensaga)

@VVK - Ich stimme teilweise nicht zu. Einige Methoden zum Beenden einer App lösen keinen onSaveInstanceState (oSIS) aus. Dies begrenzt den Nutzen von oSIS. Es lohnt sich zu unterstützen, für minimale Betriebssystemressourcen. Wenn eine App jedoch den Benutzer in den Status zurückversetzen möchte, in dem er sich befand, unabhängig davon, wie die App beendet wurde, muss stattdessen ein permanenter Speicheransatz verwendet werden. Ich verwende onCreate, um nach Bundle zu suchen. Wenn es fehlt, überprüfe ich den permanenten Speicher. Dies zentralisiert die Entscheidungsfindung. Ich kann mich von einem Absturz oder einem Zurück-Button-Exit oder einem benutzerdefinierten Menüelement Exit erholen oder zum Bildschirmbenutzer zurückkehren, der sich viele Tage später befand. - ToolmakerSteve 19. September 15 um 10:38


onSaveInstanceState()für transiente Daten (wiederhergestellt in onCreate()/ onRestoreInstanceState()), onPause()für persistente Daten (wiederhergestellt in onResume()). Aus technischen Android-Ressourcen:

onSaveInstanceState () wird von Android aufgerufen, wenn die Aktivität gestoppt wird, und möglicherweise beendet, bevor sie fortgesetzt wird! Das heißt, es sollte alle erforderlichen Zustände speichern, die erforderlich sind, um beim Neustart der Aktivität den gleichen Zustand wiederherzustellen. Es ist das Gegenstück zur onCreate () -Methode. Tatsächlich ist das gespeicherteInstanceState-Bundle, das an onCreate () übergeben wurde, dasselbe Bundle, das Sie als outState in der onSaveInstanceState () -Methode erstellen.

onPause () und onResume () sind ebenfalls komplementäre Methoden. onPause () wird immer dann aufgerufen, wenn die Aktivität endet, selbst wenn wir (beispielsweise mit einem finish () - Aufruf) diesen Aufruf initiiert haben. Wir werden dies verwenden, um die aktuelle Notiz in der Datenbank zu speichern. Es empfiehlt sich, alle Ressourcen freizugeben, die während einer onPause () -Eigenschaft freigegeben werden können, um im passiven Zustand weniger Ressourcen zu beanspruchen.


Mein Problem war, dass ich nur während der Anwendungslebensdauer Persistenz benötigte (dh eine einzelne Ausführung, einschließlich des Startens anderer Unteraktivitäten innerhalb derselben App und Drehen des Geräts usw.). Ich habe verschiedene Kombinationen der obigen Antworten ausprobiert, aber nicht in allen Situationen das bekommen, was ich wollte. Am Ende funktionierte es für mich, während onCreate einen Verweis auf den SavedInstanceState zu erhalten:

mySavedInstanceState=savedInstanceState;

und verwenden Sie das, um den Inhalt meiner Variablen zu erhalten, wenn ich es brauchte, wie folgt:

if (mySavedInstanceState !=null) {
   boolean myVariable = mySavedInstanceState.getBoolean("MyVariable");
}

Ich benutze onSaveInstanceStateund onRestoreInstanceStatewie oben vorgeschlagen, aber ich denke, ich könnte meine Methode auch alternativ verwenden, um die Variable zu speichern, wenn sie sich ändert (zB using putBoolean).


Eine Aktivität neu erstellen

Es gibt einige Szenarien, in denen Ihre Aktivität aufgrund eines normalen App-Verhaltens zerstört wird, z. B. wenn der Benutzer die Zurück-Taste drückt oder Ihre Aktivität durch den Aufruf von finish() eine eigene Zerstörung signalisiert. Das System kann Ihre Aktivitäten auch zerstören, wenn es momentan angehalten wurde und längere Zeit nicht verwendet wurde oder die Vordergrundaktivität mehr Ressourcen erfordert. Das System muss daher Hintergrundprozesse herunterfahren, um den Speicher wiederherzustellen.

Wenn Ihre activity zerstört wird, weil der Benutzer Zurück drückt oder die activity selbst beendet, ist das Systemkonzept dieser Activity für immer verschwunden, da das Verhalten darauf hinweist, dass die Aktivität nicht mehr benötigt wird. Wenn das System die Aktivität jedoch aufgrund von Systemeinschränkungen (und nicht dem normalen Verhalten der App) zerstört, merkt sich das System, obwohl die eigentliche Activity-Instanz nicht mehr vorhanden ist, dass die Aktivität existiert, so dass das System, wenn der Benutzer zu ihm zurück navigiert, ein neues Objekt erstellt Instanz der Aktivität, die einen Satz gespeicherter Daten verwendet, der den Status der Aktivität beschreibt, als sie destroyed . Die gespeicherten Daten, die das System zum Wiederherstellen des vorherigen Status verwendet, werden als "Instanzstatus" bezeichnet und sind eine Sammlung von Schlüsselwertpaaren, die in einem Bündelobjekt gespeichert sind.

Um zusätzliche Daten zum Aktivitätsstatus zu speichern, müssen Sie die Callback-Methode onSaveInstanceState () überschreiben. Das System ruft diese Methode auf, wenn der Benutzer Ihre Aktivität verlässt, und übergibt das Bundle-Objekt, das gespeichert wird, falls Ihre Aktivität unerwartet zerstört wird. Wenn das System die Aktivitätsinstanz später erneut erstellen muss, übergibt es dasselbe Bundle-Objekt an die onRestoreInstanceState() und onCreate() .

Wenn das System mit dem Anhalten Ihrer Aktivität beginnt, ruft es onSaveInstanceState() (1) auf, sodass Sie zusätzliche onSaveInstanceState() angeben können, die Sie speichern onSaveInstanceState() , falls die Activity-Instanz neu erstellt werden muss. Wenn die Aktivität zerstört wird und dieselbe Instanz erneut erstellt werden muss, übergibt das System die unter (1) definierten Zustandsdaten sowohl an die onCreate() Methode (2) als auch an die onRestoreInstanceState() Methode (3).

Speichern Sie Ihren Activity

Wenn Ihre Aktivität zu stoppen beginnt, ruft das System onSaveInstanceState() damit Ihre Aktivität onSaveInstanceState() mit einer Sammlung von Schlüssel-Wert-Paaren speichern kann. Bei der Standardimplementierung dieser Methode werden Informationen zum Status der Ansichtshierarchie der Aktivität EditText , beispielsweise der Text in einem EditText Widget oder die ListView einer ListView .

Um zusätzliche onSaveInstanceState() für Ihre Aktivität zu speichern, müssen Sie onSaveInstanceState() implementieren und dem Bundle-Objekt Schlüsselwertpaare hinzufügen. Zum Beispiel:

  static final String STATE_SCORE = "playerScore";
  static final String STATE_LEVEL = "playerLevel";

  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
  // Save the user's current game state
  savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
  savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);

  // Always call the superclass so it can save the view hierarchy state
  super.onSaveInstanceState(savedInstanceState);
}

Achtung: Rufen Sie immer die Superklassenimplementierung von onSaveInstanceState() damit die Standardimplementierung den Status der Ansichtshierarchie speichern kann.

Stellen Sie Ihren Activity

Wenn Ihre Aktivität neu erstellt wird, nachdem sie zuvor zerstört wurde, können Sie Ihren gespeicherten Status aus dem Bündel wiederherstellen, in dem das System Ihre Aktivität durchläuft. Die Callback-Methoden onCreate() und onRestoreInstanceState() empfangen dasselbe Bundle , das die Informationen zum onCreate() enthält.

Da die onCreate() -Methode aufgerufen wird, unabhängig davon, ob das System eine neue Instanz Ihrer Aktivität erstellt oder eine vorherige erstellt, müssen Sie prüfen, ob der Zustand Bundle NULL ist, bevor Sie versuchen, sie zu lesen. Wenn es null ist, erstellt das System eine neue Instanz der Aktivität, anstatt eine vorher zerstörte wiederherzustellen.

Zum Beispiel können Sie einige onCreate() in onCreate() wiederherstellen:

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); // Always call the superclass first

 // Check whether we're recreating a previously destroyed instance
 if (savedInstanceState != null) {
    // Restore value of members from saved state
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
 } else {
    // Probably initialize members with default values for a new instance
 }

 }

Anstatt den Zustand während onCreate() wiederherzustellen, können Sie auch onRestoreInstanceState() implementieren, das das System nach der onStart() Methode onStart() . Das System ruft onRestoreInstanceState() nur dann auf, wenn ein wiederhergestellter Zustand gespeichert ist. Sie müssen also nicht prüfen, ob das Bundle null ist:

  public void onRestoreInstanceState(Bundle savedInstanceState) {
  // Always call the superclass so it can restore the view hierarchy
  super.onRestoreInstanceState(savedInstanceState);

  // Restore state members from saved instance
  mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
  mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

onSaveInstanceState wird aufgerufen, wenn das System Speicher benötigt und eine Anwendung beendet wird. Es wird nicht aufgerufen, wenn der Benutzer die Anwendung gerade schließt. Ich denke, der Anwendungsstatus sollte auch in onPause Es sollte in einem permanenten Speicher wie Preferences oder Sqlite


Um Daten zum Aktivitätsstatus onCreate()abzurufen, müssen Sie die Daten zuerst in saveInstanceState speichern, indem Sie die SaveInstanceState(Bundle savedInstanceState)Methode überschreiben .

Wenn die Aktivität destroy- SaveInstanceState(Bundle savedInstanceState)Methode aufgerufen wird und dort die Daten speichert, die Sie speichern möchten. Sie erhalten dasselbe, onCreate()wenn die Aktivität neu gestartet wird.


Dies ist eine klassische "Gotcha" der Android-Entwicklung. Hier gibt es zwei Probleme:

  • Es gibt einen subtilen Android-Framework-Fehler, der das Application-Stack-Management während der Entwicklung erheblich erschwert, zumindest bei älteren Versionen (nicht ganz sicher, ob / wann / wie es behoben wurde). Ich werde diesen Fehler unten besprechen.
  • Der "normale" oder beabsichtigte Weg, dieses Problem zu lösen, ist an sich ziemlich kompliziert mit der Dualität von onPause / onResume und onSaveInstanceState / onRestoreInstanceState

Beim Durchstöbern all dieser Threads vermute ich, dass Entwickler in den meisten Fällen gleichzeitig über diese zwei verschiedenen Themen sprechen ... daher all die Verwirrung und Berichte über "das funktioniert nicht für mich".

Erstens, um das "beabsichtigte" Verhalten zu klären: onSaveInstance und onRestoreInstance sind fragil und nur für einen vorübergehenden Zustand. Die beabsichtigte Verwendung (Affekt) ist die Durchführung der Aktivitätserholung, wenn das Telefon gedreht wird (Ausrichtungsänderung). Mit anderen Worten, die beabsichtigte Verwendung liegt vor, wenn Ihre Aktivität immer noch logisch "oben" ist, jedoch vom System erneut bestätigt werden muss. Das gespeicherte Bundle wird nicht außerhalb des Prozesses / Speichers / GC gespeichert. Sie können sich also nicht wirklich darauf verlassen, wenn Ihre Aktivitäten in den Hintergrund treten. Ja, vielleicht überlebt der Speicher Ihrer Aktivität die Reise in den Hintergrund und entgeht der GC, aber dies ist nicht zuverlässig (und auch nicht vorhersehbar).

Wenn Sie also ein Szenario haben, in dem sinnvolle "Benutzerfortschritte" oder Zustände bestehen, die zwischen den "Startvorgängen" Ihrer Anwendung bestehen bleiben sollen, sollten Sie onPause und onResume verwenden. Sie müssen selbst einen dauerhaften Speicher auswählen und vorbereiten.

ABER - es gibt einen sehr verwirrenden Fehler, der all dies kompliziert. Details finden Sie hier:

http://code.google.com/p/android/issues/detail?id=2373

http://code.google.com/p/android/issues/detail?id=5277

Wenn Ihre Anwendung mit dem SingleTask-Flag gestartet wird und Sie sie später über den Startbildschirm oder das Startmenü starten, wird durch den folgenden Aufruf eine NEUE Aufgabe erstellt ... Sie haben praktisch zwei verschiedene Instanzen Ihrer App den gleichen Stack bewohnen ... was sehr schnell sehr seltsam wird. Dies scheint zu passieren, wenn Sie Ihre App während der Entwicklung starten (z. B. von Eclipse oder Intellij). Daher stoßen Entwickler viel darauf. Aber auch durch einige der App Store-Aktualisierungsmechanismen (so wirkt sich dies auch auf Ihre Benutzer aus).

Ich habe mich stundenlang durch diese Threads gekämpft, bevor mir klar wurde, dass mein Hauptaugenmerk auf diesen Fehler und nicht auf das beabsichtigte Rahmenverhalten gerichtet war. Eine tolle Kritik und Problemumgehung (UPDATE: siehe unten) scheint in dieser Antwort von Benutzer @kaciula zu stammen:

Home Tastendruck Verhalten

UPDATE Juni 2013 : Monate später habe ich endlich die 'richtige' Lösung gefunden. Sie müssen keine stateful-startApp-Flags selbst verwalten, Sie können dies aus dem Framework herausfinden und entsprechend entlassen. Ich verwende dies am Anfang meiner LauncherActivity.onCreate:

if (!isTaskRoot()) {
    Intent intent = getIntent();
    String action = intent.getAction();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
        finish();
        return;
    }
}

Es gibt grundsätzlich zwei Möglichkeiten, diese Änderung umzusetzen.

  1. mit onSaveInstanceState()und onRestoreInstanceState().
  2. In manifestieren android:configChanges="orientation|screenSize".

Ich empfehle wirklich nicht, die zweite Methode zu verwenden. Denn in einer meiner Erfahrungen verursachte es, dass die Hälfte des Bildschirms schwarz wurde, während er vom Hochformat in das Querformat und umgekehrt gedreht wurde.

Mit der ersten oben genannten Methode können wir Daten beibehalten, wenn sich die Ausrichtung ändert oder eine Konfigurationsänderung erfolgt. Ich kenne einen Weg, auf dem Sie jede Art von Daten innerhalb des Zustandsobjekts „SavedInstance“ speichern können.

Beispiel: Betrachten Sie einen Fall, wenn Sie ein Json-Objekt beibehalten möchten. Erstellen Sie eine Modellklasse mit Gettern und Setters.

class MyModel extends Serializable{
JSONObject obj;

setJsonObject(JsonObject obj)
{
this.obj=obj;
}

JSONObject getJsonObject()
return this.obj;
} 
}

Führen Sie nun in Ihrer Aktivität in der Methode onCreate und der Methode OnSaveInstanceState Folgendes aus. Es wird ungefähr so ​​aussehen:

@override
onCreate(Bundle savedInstaceState){
MyModel data= (MyModel)savedInstaceState.getSerializable("yourkey")
JSONObject obj=data.getJsonObject();
//Here you have retained JSONObject and can use.
}


@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Obj is some json object 
MyModel dataToSave= new MyModel();
dataToSave.setJsonObject(obj);
oustate.putSerializable("yourkey",dataToSave); 

}

Mit Icepick dieses Problem schnell lösenIcepick

Richten Sie zuerst die Bibliothek in ein app/build.gradle

repositories {
  maven {url "https://clojars.org/repo/"}
}
dependencies {
  compile 'frankiesardo:icepick:3.2.0'
  provided 'frankiesardo:icepick-processor:3.2.0'
}

Sehen wir uns nun dieses Beispiel an, um den Status in Activity zu speichern

public class ExampleActivity extends Activity {
  @State String username; // This will be automatically saved and restored

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

Es funktioniert für Aktivitäten, Fragmente oder jedes Objekt, das seinen Status für ein Bündel serialisieren muss (z. B. ViewPresenters für Mörtel).

Icepick kann auch den Instanzstatuscode für benutzerdefinierte Ansichten generieren:

class CustomView extends View {
  @State int selectedPosition; // This will be automatically saved and restored

  @Override public Parcelable onSaveInstanceState() {
    return Icepick.saveInstanceState(this, super.onSaveInstanceState());
  }

  @Override public void onRestoreInstanceState(Parcelable state) {
    super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state));
  }

  // You can put the calls to Icepick into a BaseCustomView and inherit from it
  // All Views extending this CustomView automatically have state saved/restored
}

Jetzt bietet Android ViewModels zum Speichern des Status an. Sie sollten versuchen, dies anstelle von saveInstanceState zu verwenden.


Sie müssen onSaveInstanceState(Bundle savedInstanceState) und die Werte für die Anwendungsstatus, die Sie ändern möchten, in den Bundle Parameter wie onSaveInstanceState(Bundle savedInstanceState) schreiben:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // Save UI state changes to the savedInstanceState.
  // This bundle will be passed to onCreate if the process is
  // killed and restarted.
  savedInstanceState.putBoolean("MyBoolean", true);
  savedInstanceState.putDouble("myDouble", 1.9);
  savedInstanceState.putInt("MyInt", 1);
  savedInstanceState.putString("MyString", "Welcome back to Android");
  // etc.
}

Das Bundle ist im Wesentlichen eine Methode zum Speichern einer NVP-Karte ("Name-Value Pair") und wird an onCreate() und auch onRestoreInstanceState() wo Sie die Werte wie onRestoreInstanceState() extrahieren würden:

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  // Restore UI state from the savedInstanceState.
  // This bundle has also been passed to onCreate.
  boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
  double myDouble = savedInstanceState.getDouble("myDouble");
  int myInt = savedInstanceState.getInt("MyInt");
  String myString = savedInstanceState.getString("MyString");
}

Normalerweise verwenden Sie diese Technik, um Instanzwerte für Ihre Anwendung zu speichern (Auswahl, nicht gespeicherter Text usw.).


Mittlerweile nutze ich im Allgemeinen keinen Gebrauch mehr

Bundle savedInstanceState & Co

Der Live-Zyklus ist für die meisten Aktivitäten zu kompliziert und nicht notwendig. Und Google erklärt sich selbst, es ist nicht einmal zuverlässig.

Mein Weg ist, alle Änderungen sofort in den Einstellungen zu speichern

 SharedPreferences p;
 p.edit().put(..).commit()

In gewisser Weise funktionieren SharedPreferences ähnlich wie Bundles. Und natürlich müssen diese Werte zunächst von den Vorlieben rot sein.

Bei komplexen Daten können Sie Sqlite anstelle von Voreinstellungen verwenden.

Bei der Anwendung dieses Konzepts verwendet die Aktivität nur den zuletzt gespeicherten Status, unabhängig davon, ob es sich um ein anfängliches Öffnen mit dazwischen liegenden Neustarts oder um ein erneutes Öffnen aufgrund des Backstacks handelt.


Zustand zu retten ist für mich bestenfalls ein Kludge. Wenn Sie persistente Daten speichern möchten, verwenden Sie einfach eine SQLite Datenbank. Android macht es so einfach.

Etwas wie das:

import java.util.Date;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class dataHelper {

    private static final String DATABASE_NAME = "autoMate.db";
    private static final int DATABASE_VERSION = 1;

    private Context context;
    private SQLiteDatabase db;
    private OpenHelper oh ;

    public dataHelper(Context context) {
        this.context = context;
        this.oh = new OpenHelper(this.context);
        this.db = oh.getWritableDatabase();
    }

    public void close()
    {
        db.close();
        oh.close();
        db = null;
        oh = null;
        SQLiteDatabase.releaseMemory();
    }


    public void setCode(String codeName, Object codeValue, String codeDataType)
    {
        Cursor codeRow = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        String cv = "" ;

        if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            cv = String.valueOf(((Date)codeValue).getTime());
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            String.valueOf(codeValue);
        }
        else
        {
            cv = String.valueOf(codeValue);
        }

        if(codeRow.getCount() > 0) //exists-- update
        {
            db.execSQL("update code set codeValue = '" + cv +
                "' where codeName = '" + codeName + "'");
        }
        else // does not exist, insert
        {
            db.execSQL("INSERT INTO code (codeName, codeValue, codeDataType) VALUES(" +
                    "'" + codeName + "'," +
                    "'" + cv + "'," +
                    "'" + codeDataType + "')" );
        }
    }

    public Object getCode(String codeName, Object defaultValue)
    {
        //Check to see if it already exists
        String codeValue = "";
        String codeDataType = "";
        boolean found = false;
        Cursor codeRow  = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        if (codeRow.moveToFirst())
        {
            codeValue = codeRow.getString(codeRow.getColumnIndex("codeValue"));
            codeDataType = codeRow.getString(codeRow.getColumnIndex("codeDataType"));
            found = true;
        }

        if (found == false)
        {
            return defaultValue;
        }
        else if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (long)0;
            }
            return Long.parseLong(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (int)0;
            }
            return Integer.parseInt(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            if (codeValue.equals("") == true)
            {
                return null;
            }
            return new Date(Long.parseLong(codeValue));
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            if (codeValue.equals("") == true)
            {
                return false;
            }
            return Boolean.parseBoolean(codeValue);
        }
        else
        {
            return (String)codeValue;
        }
    }


    private static class OpenHelper extends SQLiteOpenHelper {

        OpenHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE IF  NOT EXISTS code" +
            "(id INTEGER PRIMARY KEY, codeName TEXT, codeValue TEXT, codeDataType TEXT)");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
}

Ein einfacher Anruf danach

dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;

Nicht sicher, ob meine Lösung missbilligt wird oder nicht, aber ich verwende einen gebundenen Dienst, um den ViewModel-Status beizubehalten. Ob Sie es im Dienst im Speicher ablegen oder aus einer SqlLite-Datenbank abrufen und abrufen, hängt von Ihren Anforderungen ab. Dies ist, was Dienste jeder Art tun, sie bieten Dienste wie die Aufrechterhaltung des Anwendungsstatus und abstrakte allgemeine Geschäftslogik.

Aufgrund von Speicher- und Verarbeitungsbeschränkungen bei mobilen Geräten behandle ich Android-Ansichten ähnlich wie eine Webseite. Die Seite behält den Status nicht bei, es handelt sich lediglich um eine Präsentationsschichtkomponente, deren einziger Zweck darin besteht, den Anwendungsstatus darzustellen und Benutzereingaben zu akzeptieren. Jüngste Trends in der Web-App-Architektur verwenden das uralte MVC-Muster (Model, View, Controller), bei dem die Seite die View ist, die Domänendaten das Modell sind und der Controller hinter einem Webdienst sitzt. Das gleiche Muster kann in Android verwendet werden, wobei die Ansicht gut ist. Die Ansicht, das Modell sind Ihre Domänendaten und der Controller ist als Android-gebundener Dienst implementiert. Wann immer Sie möchten, dass eine Ansicht mit dem Controller interagiert, binden Sie beim Starten / Fortsetzen eine Verbindung dazu und bei Stopp / Pause die Bindung auf.

Dieser Ansatz bietet Ihnen den zusätzlichen Vorteil der Durchsetzung des Konstruktionsprinzips "Separation of Concern", da die gesamte Geschäftslogik Ihrer Anwendung in Ihren Service verschoben werden kann, wodurch doppelte Logik in mehreren Ansichten reduziert wird und die Ansicht die Erzwingung eines weiteren wichtigen Konstruktionsprinzips, "Single Responsibility", ermöglicht.


Um die ursprüngliche Frage direkt zu beantworten. savedInstancestate ist null, da Ihre Aktivität niemals neu erstellt wird.

Ihre Aktivität wird nur mit einem Statuspaket neu erstellt, wenn:

  • Konfigurationsänderungen, z. B. Ändern der Ausrichtung oder der Telefonsprache, sodass möglicherweise eine neue Aktivitätsinstanz erstellt wird.
  • Sie kehren aus dem Hintergrund zur App zurück, nachdem das Betriebssystem die Aktivität zerstört hat.

Android zerstört Hintergrundaktivitäten, wenn es unter Gedächtnisdruck steht oder nachdem es längere Zeit im Hintergrund war.

Beim Testen Ihres Hallo-Welt-Beispiels gibt es einige Möglichkeiten, die Aktivität zu verlassen und wieder zurückzukehren.

  • Wenn Sie die Zurück-Taste drücken, ist die Aktivität beendet. Das erneute Starten der App ist eine brandneue Instanz. Sie setzen den Hintergrund überhaupt nicht fort.
  • Wenn Sie die Home-Taste drücken oder den Task-Switcher verwenden, wechselt die Aktivität in den Hintergrund. Wenn Sie zur Anwendung zurück navigieren, wird onCreate nur aufgerufen, wenn die Aktivität zerstört werden muss.

In den meisten Fällen muss die Aktivität nicht neu erstellt werden, wenn Sie einfach auf Home drücken und dann die App erneut starten. Es ist bereits im Speicher vorhanden, so dass onCreate () nicht aufgerufen wird.

Unter Einstellungen -> Entwickleroptionen gibt es eine Option namens "Aktivitäten nicht beibehalten". Wenn diese Option aktiviert ist, zerstört Android immer Aktivitäten und erstellt sie im Hintergrund neu. Dies ist eine großartige Option, die bei der Entwicklung aktiviert bleiben sollte, da sie den Worst-Case-Fall simuliert. (Ein Gerät mit wenig Speicherplatz, das Ihre Aktivitäten ständig wiederverwendet).

Die anderen Antworten sind insofern wertvoll, als sie Ihnen die richtigen Methoden zum Speichern des Zustands beibringen, aber ich hatte nicht das Gefühl, dass sie wirklich geantwortet haben, warum Ihr Code nicht so funktioniert, wie Sie es erwartet haben.


Beachten Sie, dass die Verwendung von onSaveInstanceState und onRestoreInstanceState für persistente Daten NICHT sicher ist, wie in der Dokumentation zu Aktivitätsstatus in developer.android.com/reference/android/app/Activity.html .

Das Dokument gibt an (im Abschnitt "Aktivitätslebenszyklus"):

Beachten Sie, dass es wichtig ist, persistente Daten in onPause() anstelle von onSaveInstanceState(Bundle) zu speichern, da die späteren nicht Teil der Lifecycle-Callbacks sind und daher nicht in jeder Situation aufgerufen werden, wie in der Dokumentation beschrieben.

Mit anderen Worten, setzen Sie Ihren Speicher- / Wiederherstellungscode für persistente Daten in onPause() und onResume() !

BEARBEITEN : Zur weiteren Verdeutlichung die Dokumentation zu onSaveInstanceState() :

Diese Methode wird aufgerufen, bevor eine Aktivität abgebrochen wird, damit sie ihren Status wiederherstellen kann, wenn sie in der Zukunft zurückkehrt. Wenn beispielsweise Aktivität B vor Aktivität A gestartet wird und Aktivität A zu einem bestimmten Zeitpunkt beendet wird, um Ressourcen zurückzugewinnen, hat Aktivität A die Möglichkeit, den aktuellen Status der Benutzeroberfläche über diese Methode zu speichern, wenn der Benutzer zurückkehrt In Aktivität A kann der Zustand der Benutzeroberfläche über onCreate(Bundle) oder onRestoreInstanceState(Bundle) wiederhergestellt werden.


Ich glaube, ich habe die Antwort gefunden. Lassen Sie mich mit einfachen Worten sagen, was ich getan habe:

Angenommen, ich habe zwei Aktivitäten, Aktivität1 und Aktivität2, und ich navigiere von Aktivität1 zu Aktivität2 (Ich habe einige Arbeiten in Aktivität2 ausgeführt) und wieder zu Aktivität 1, indem ich auf eine Schaltfläche in Aktivität1 klicke. Zu diesem Zeitpunkt wollte ich zu Activity2 zurückkehren und möchte meine Activity2 in demselben Zustand sehen, wenn ich Activity2 zuletzt verlassen habe.

Für das obige Szenario habe ich folgende Änderungen vorgenommen:

<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>

Und in der activity1 auf dem Button-Click-Event habe ich folgendes gemacht:

Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);

Und in activity2 on button click habe ich folgendes gemacht:

Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);

Was nun passieren wird, ist, dass die Änderungen, die wir in der Aktivität2 vorgenommen haben, nicht verloren gehen, und wir können Aktivität2 in dem Zustand sehen, den wir zuvor verlassen haben.

Ich glaube, das ist die Antwort und das funktioniert gut für mich. Korrigiere mich, wenn ich falsch liege.


Hinzufügen von LiveData (Android-Architekturkomponenten) zu Ihrem Projekt

Fügen Sie die folgende Abhängigkeit hinzu

implementation "android.arch.lifecycle:extensions:1.1.0"

LiveData nimmt einen Beobachter auf und benachrichtigt ihn nur dann, wenn sich die Daten im Status STARTED oder RESUMED befinden. Der Vorteil von LiveData besteht darin, dass die Aktivität onChanged für den Beobachter nicht aufgerufen wird, wenn Ihre Aktivität in einen anderen Status als STARTED oder RESUMED wechselt .

private TextView mTextView;
private MutableLiveData<String> mMutableLiveData;

@Override
protected void onCreate(Bundle savedInstanceState) {
    mTextView = (TextView) findViewById(R.id.textView);
    mMutableLiveData = new MutableLiveData<>();
    mMutableLiveData.observe(this, new Observer<String>() {
        @Override
        public void onChanged(@Nullable String s) {
            mTextView.setText(s);
        }
    });

}

Der savedInstanceState dient nur zum Speichern des Zustands, der einer aktuellen Instanz einer Aktivität zugeordnet ist, z. B. der aktuellen Navigation oder Auswahlinformationen. Wenn also Android eine Aktivität zerstört und neu erstellt, kann sie wie zuvor wiederhergestellt werden. Weitere onCreate Sie in der Dokumentation zu onCreate und onSaveInstanceState

Für eine längere Lebensdauer sollten Sie die Verwendung einer SQLite-Datenbank, einer Datei oder von Voreinstellungen in Betracht ziehen. Siehe Dauerzustand speichern .


Die Methoden onSaveInstanceState(bundle)und onRestoreInstanceState(bundle)dienen zur Datenpersistenz, wenn Sie den Bildschirm drehen (Orientierungsänderung).
Sie sind nicht einmal gut , während Umschalten zwischen Anwendungen (da die onSaveInstanceState()Methode aufgerufen wird , aber onCreate(bundle)und onRestoreInstanceState(bundle)wird nicht wieder aufgerufen werden .
Für weitere Persistenz Verwendung gemeinsamer Einstellungen. Diesen Artikel lesen


Kotlin-Code:

sparen:

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState.apply {
        putInt("intKey", 1)
        putString("stringKey", "String Value")
        putParcelable("parcelableKey", parcelableObject)
    })
}

und dann in onCreate()oderonRestoreInstanceState()

    val restoredInt = savedInstanceState?.getInt("intKey") ?: 1 //default int
    val restoredString = savedInstanceState?.getString("stringKey") ?: "default string"
    val restoredParcelable = savedInstanceState?.getParcelable<ParcelableClass>("parcelableKey") ?: ParcelableClass() //default parcelable

Fügen Sie Standardwerte hinzu, wenn Sie keine Optionals verwenden möchten


Android-Emulator ist absolut langsam. Es dauert 800 MB Speicher während der Ausführung. Wenn Sie Windows verwenden, können Sie Microsoft Android Emulator verwenden. Es ist hervorragend, bietet Ihnen mehr Funktionen als Android Studio Emulator. Und am wichtigsten ist es schnell (verbraucht nur 13 MB). Es wird mit Visual Studio 2015 Technical Preview geliefert. Ich benutze es und bin damit zufrieden. Ich habe das gesamte VS-Paket heruntergeladen und installiert. Ich muss prüfen, wie wir nur den VS-Emulator installieren können.

Visual Studio Emulator für Android

BEARBEITEN: Versuchen Sie https://www.visualstudio.com/vs/msft-android-emulator/





android android-activity application-state