Salvataggio dello stato di attività Android utilizzando Salva stato istanza




xamarin forms service (18)

Ho lavorato sulla piattaforma Android SDK, ed è un po 'oscuro come salvare lo stato di un'applicazione. Quindi, dato questo piccolo re-tooling dell'esempio "Hello, 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);
  }
}

Ho pensato che sarebbe stato sufficiente per il caso più semplice, ma risponde sempre con il primo messaggio, indipendentemente da come mi allontani dall'app.

Sono sicuro che la soluzione è semplice come sovrascrivere onPause o qualcosa del genere, ma ho cercato nella documentazione per 30 minuti circa e non ho trovato nulla di ovvio.

https://code.i-harness.com


Aggiunta di LiveData (Componenti di architettura Android) al progetto

aggiungere la seguente dipendenza

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

LiveData rileva un osservatore e lo notifica in merito alle modifiche dei dati solo quando è nello stato STARTED o RESUMED. Il vantaggio con LiveData è che quando la tua attività entra in uno stato diverso da STARTED o RESUMED non chiamerà il metodo onChanged sull'osservatore .

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

}

È necessario eseguire l'override di onSaveInstanceState(Bundle savedInstanceState) e scrivere i valori dello stato dell'applicazione che si desidera modificare nel parametro Bundle questo modo:

@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.
}

Il Bundle è essenzialmente un modo per memorizzare una mappa NVP ("Name-Value Pair"), e passerà a onCreate() e anche onRestoreInstanceState() dove onRestoreInstanceState() i valori in questo modo:

@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");
}

Solitamente si utilizza questa tecnica per memorizzare i valori di istanza per l'applicazione (selezioni, testo non salvato, ecc.).


Il mio collega ha scritto un articolo che spiega lo stato dell'applicazione sui dispositivi Android, comprese le spiegazioni sul ciclo di vita delle attività e le informazioni sullo stato, come memorizzare le informazioni sullo stato e il salvataggio sul Bundle stato e sulle SharedPreferences e dare un'occhiata qui .

L'articolo copre tre approcci:

Memorizza i dati di controllo dell'intervallo varabile / UI per la durata dell'applicazione (ad esempio temporaneamente) utilizzando Instance State Bundle

[Code sample – Store State in State Bundle]
@Override
public void onSaveInstanceState(Bundle savedInstanceState) 
{
  // Store UI state to the savedInstanceState.
  // This bundle will be passed to onCreate on next call.  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  savedInstanceState.putString(“Name”, strName);
  savedInstanceState.putString(“Email”, strEmail);
  savedInstanceState.putBoolean(“TandC”, blnTandC);

  super.onSaveInstanceState(savedInstanceState);
}

Memorizza i dati di controllo della variabile locale / UI tra le istanze dell'applicazione (ovvero in modo permanente) utilizzando le preferenze condivise

[Code sample – Store State in SharedPreferences]
@Override
protected void onPause() 
{
  super.onPause();

  // Store values between instances here
  SharedPreferences preferences = getPreferences(MODE_PRIVATE);
  SharedPreferences.Editor editor = preferences.edit();  // Put the values from the UI
  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  editor.putString(“Name”, strName); // value to store
  editor.putString(“Email”, strEmail); // value to store
  editor.putBoolean(“TandC”, blnTandC); // value to store    
  // Commit to storage
  editor.commit();
}

Mantenere le istanze degli oggetti attive in memoria tra le attività all'interno della vita dell'applicazione utilizzando Istanza di non configurazione mantenuta

[Code sample – store object instance]
private cMyClassType moInstanceOfAClass;// Store the instance of an object
@Override
public Object onRetainNonConfigurationInstance() 
{
  if (moInstanceOfAClass != null) // Check that the object exists
      return(moInstanceOfAClass);
  return super.onRetainNonConfigurationInstance();
}

Penso di aver trovato la risposta. Lasciami dire cosa ho fatto con parole semplici:

Supponiamo che io abbia due attività, attività1 e attività2 e sto navigando dall'attività1 all'attività2 (ho svolto alcuni lavori nell'attività 2) e di nuovo all'attività 1 facendo clic su un pulsante in attività1. Ora a questo punto volevo tornare all'attività2 e voglio vedere la mia attività2 nella stessa condizione in cui ho lasciato l'attività2.

Per lo scenario sopra riportato, quello che ho fatto è che nel manifest ho apportato alcune modifiche come questa:

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

E nell'attività1 dell'evento click del pulsante ho fatto così:

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

E in activity2 all'evento click del pulsante ho fatto così:

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

Ora ciò che accadrà è che qualunque sia il cambiamento che abbiamo fatto nell'attività2 non andrà perso, e possiamo vedere l'attività2 nello stesso stato che abbiamo lasciato in precedenza.

Credo che questa sia la risposta e questo funziona bene per me. Correggimi se sbaglio


Salvare lo stato è un trauma al massimo per quanto mi riguarda. Se è necessario salvare i dati persistenti, basta usare un database SQLite . Android rende SOOO facile.

Qualcosa come questo:

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) {
        }
    }
}

Una semplice chiamata dopo

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

Si noti che NON è sicuro utilizzare onSaveInstanceState e onRestoreInstanceState per i dati persistenti , in base alla documentazione sugli stati di attività in developer.android.com/reference/android/app/Activity.html .

Il documento indica (nella sezione 'Attività ciclo di vita'):

Si noti che è importante salvare i dati persistenti in onPause() invece di onSaveInstanceState(Bundle) perché il successivo non fa parte dei callback del ciclo di vita, quindi non verrà chiamato in tutte le situazioni come descritto nella relativa documentazione.

In altre parole, inserisci il tuo codice di salvataggio / ripristino per i dati persistenti in onPause() e onResume() !

EDIT : per ulteriori chiarimenti, ecco la documentazione di onSaveInstanceState() :

Questo metodo viene chiamato prima che un'attività possa essere uccisa in modo che, quando ritorna in futuro, possa ripristinarne lo stato. Ad esempio, se l'attività B viene avviata di fronte all'attività A e ad un certo punto l'attività A viene uccisa per recuperare risorse, l'attività A avrà la possibilità di salvare lo stato corrente della sua interfaccia utente tramite questo metodo in modo che quando l'utente ritorna all'attività A, lo stato dell'interfaccia utente può essere ripristinato tramite onCreate(Bundle) o onRestoreInstanceState(Bundle) .


onSaveInstanceState viene chiamato quando il sistema ha bisogno di memoria e uccide un'applicazione. Non viene chiamato quando l'utente chiude l'applicazione. Quindi penso che lo stato dell'applicazione dovrebbe anche essere salvato in onPause Dovrebbe essere salvato in un archivio persistente come Preferences o Sqlite


savedInstanceState serve solo per salvare lo stato associato a un'istanza corrente di un'attività, ad esempio le informazioni di navigazione o selezione correnti, in modo che se Android distrugge e ricrea un'attività, può tornare come era prima. Vedere la documentazione per onCreate e onSaveInstanceState

Per uno stato di maggiore durata, prendere in considerazione l'utilizzo di un database SQLite, un file o le preferenze. Vedere Salvataggio dello stato persistente .


Codice Kotlin:

salvare:

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

e poi in onCreate()oonRestoreInstanceState()

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

Aggiungi valori predefiniti se non vuoi avere Optionals


Esistono fondamentalmente due modi per attuare questo cambiamento.

  1. usando onSaveInstanceState()e onRestoreInstanceState().
  2. In manifest android:configChanges="orientation|screenSize".

Io davvero non consiglio di usare il secondo metodo. Dal momento che in una delle mie esperienze stava causando la metà dello schermo del dispositivo nero durante la rotazione da verticale a orizzontale e viceversa.

Usando il primo metodo menzionato sopra, possiamo mantenere i dati quando l'orientamento è cambiato o qualsiasi cambiamento di configurazione accade. Conosco un modo in cui è possibile memorizzare qualsiasi tipo di dati all'interno dell'oggetto stato salvato.

Esempio: considera un caso se vuoi mantenere l'oggetto Json. creare una classe modello con getter e setter.

class MyModel extends Serializable{
JSONObject obj;

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

JSONObject getJsonObject()
return this.obj;
} 
}

Ora nella tua attività nel metodo onCreate e onSaveInstanceState fai quanto segue. Sembrerà qualcosa del genere:

@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); 

}

Quando viene creata un'attività, viene chiamato il metodo onCreate ().

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

savedInstanceState è un oggetto della classe Bundle che è nullo per la prima volta, ma contiene valori quando viene ricreato. Per salvare lo stato di Activity devi sovrascrivere onSaveInstanceState ().

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

metti i tuoi valori nell'oggetto Bundle "outState" come outState.putString ("key", "Bentornato") e salva chiamando super. Quando l'attività viene distrutta, lo stato viene salvato nell'oggetto Bundle e può essere ripristinato dopo la ricreazione in onCreate () o onRestoreInstanceState (). Il pacchetto ricevuto in onCreate () e onRestoreInstanceState () sono gli stessi.

   @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");
            }
    }

o

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

Davvero onSaveInstanceCallen stato quando l'attività va a fondo

Citazione dai documenti: "il metodo onSaveInstanceState(Bundle)viene chiamato prima di posizionare l'attività in tale stato di sfondo"


I metodi onSaveInstanceState(bundle)e onRestoreInstanceState(bundle)sono utili per la persistenza dei dati semplicemente ruotando lo schermo (cambio di orientamento).
Essi non sono nemmeno buono durante il passaggio tra le applicazioni (dato che il onSaveInstanceState()metodo è chiamato ma onCreate(bundle)e onRestoreInstanceState(bundle)non si richiama nuovamente.
Per più uso persistenza preferenze condivise. Leggere questo articolo


Il mio problema era che avevo bisogno di persistenza solo durante la vita dell'applicazione (cioè una singola esecuzione che includesse l'avvio di altre sotto-attività all'interno della stessa app e la rotazione del dispositivo, ecc.). Ho provato varie combinazioni delle risposte di cui sopra, ma non ho ottenuto ciò che volevo in tutte le situazioni. Alla fine, ciò che ha funzionato per me è stato ottenere un riferimento all'InstanceState salvato durante onCreate:

mySavedInstanceState=savedInstanceState;

e lo uso per ottenere il contenuto della mia variabile quando ne avevo bisogno, sulla falsariga di:

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

Io uso onSaveInstanceStatee onRestoreInstanceStatecome suggerito sopra, ma suppongo che potrei anche o in alternativa usare il mio metodo per salvare la variabile quando cambia (ad esempio usando putBoolean)


Ora Android fornisce ViewModels per il salvataggio dello stato, dovresti provare a utilizzarlo invece di saveInstanceState.


Per aiutare a ridurre il boilerplate, uso quanto segue interfacee classper leggere / scrivere su a Bundleper salvare lo stato dell'istanza.

Innanzitutto, crea un'interfaccia che verrà utilizzata per annotare le tue variabili di istanza:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.FIELD
})
public @interface SaveInstance {

}

Quindi, crea una classe in cui verrà utilizzata la reflection per salvare i valori nel bundle:

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;

import java.io.Serializable;
import java.lang.reflect.Field;

/**
 * Save and load fields to/from a {@link Bundle}. All fields should be annotated with {@link
 * SaveInstance}.</p>
 */
public class Icicle {

    private static final String TAG = "Icicle";

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #load(Bundle, Object)
     */
    public static void save(Bundle outState, Object classInstance) {
        save(outState, classInstance, classInstance.getClass());
    }

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #load(Bundle, Object, Class)
     */
    public static void save(Bundle outState, Object classInstance, Class<?> baseClass) {
        if (outState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    field.setAccessible(true);
                    String key = className + "#" + field.getName();
                    try {
                        Object value = field.get(classInstance);
                        if (value instanceof Parcelable) {
                            outState.putParcelable(key, (Parcelable) value);
                        } else if (value instanceof Serializable) {
                            outState.putSerializable(key, (Serializable) value);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not added to the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #save(Bundle, Object)
     */
    public static void load(Bundle savedInstanceState, Object classInstance) {
        load(savedInstanceState, classInstance, classInstance.getClass());
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #save(Bundle, Object, Class)
     */
    public static void load(Bundle savedInstanceState, Object classInstance, Class<?> baseClass) {
        if (savedInstanceState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    String key = className + "#" + field.getName();
                    field.setAccessible(true);
                    try {
                        Object fieldVal = savedInstanceState.get(key);
                        if (fieldVal != null) {
                            field.set(classInstance, fieldVal);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not retrieved from the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

}

Esempio di utilizzo:

public class MainActivity extends Activity {

    @SaveInstance
    private String foo;

    @SaveInstance
    private int bar;

    @SaveInstance
    private Intent baz;

    @SaveInstance
    private boolean qux;

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

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

}

Nota: questo codice è stato adattato da un progetto di libreria denominato AndroidAutowire concesso in licenza con la licenza MIT .


Per rispondere direttamente alla domanda originale. savedInstancestate è nullo perché la tua attività non viene mai ricreata.

La tua attività verrà ricreata solo con un pacchetto di stato quando:

  • Modifiche alla configurazione come la modifica dell'orientamento o della lingua del telefono che potrebbe richiedere la creazione di una nuova istanza di attività.
  • Si ritorna all'app dallo sfondo dopo che il sistema operativo ha distrutto l'attività.

Android distruggerà le attività in background quando si trova sotto pressione di memoria o dopo essere stati in background per un lungo periodo di tempo.

Durante il test del tuo esempio ciao mondo ci sono alcuni modi per andarsene e tornare all'attività.

  • Quando si preme il pulsante Indietro, l'attività è terminata. Rilanciare l'app è un'istanza nuova di zecca. Non stai affatto riprendendo dallo sfondo.
  • Quando si preme il pulsante home o si utilizza il commutatore di attività, l'attività verrà spostata sullo sfondo. Quando si ritorna all'applicazione onCreate verrà chiamato solo se l'attività deve essere distrutta.

Nella maggior parte dei casi, se stai semplicemente premendo a casa e poi lanciando di nuovo l'app, non è necessario ricreare l'attività. Esiste già in memoria così onCreate () non verrà chiamato.

C'è un'opzione in Impostazioni -> Opzioni sviluppatore chiamata "Non mantenere attività". Quando è abilitato, Android distruggerà sempre le attività e le ricreerà quando saranno in background. Questa è un'ottima opzione per lasciare abilitato durante lo sviluppo perché simula lo scenario peggiore. (Un dispositivo a bassa memoria che ricicla le tue attività in ogni momento).

Le altre risposte sono preziose in quanto ti insegnano i modi corretti per memorizzare lo stato, ma non ho sentito che hanno risposto veramente PERCHÉ il tuo codice non funzionava nel modo previsto.


Sebbene la risposta accettata sia corretta, esiste un metodo più rapido e semplice per salvare lo stato di attività su Android utilizzando una libreria denominata Icepick . Icepick è un processore di annotazioni che si prende cura di tutto il codice boilerplate utilizzato per salvare e ripristinare lo stato per te.

Fare qualcosa come questo con Icepick:

class MainActivity extends Activity {
  @State String username; // These will be automatically saved and restored
  @State String password;
  @State int age;

  @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);
  }
}

È lo stesso di fare questo:

class MainActivity extends Activity {
  String username;
  String password;
  int age;

  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putString("MyString", username);
    savedInstanceState.putString("MyPassword", password);
    savedInstanceState.putInt("MyAge", age); 
    /* remember you would need to actually initialize these variables before putting it in the
    Bundle */
  }

  @Override
  public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    username = savedInstanceState.getString("MyString");
    password = savedInstanceState.getString("MyPassword");
    age = savedInstanceState.getInt("MyAge");
  }
}

Icepick funzionerà con qualsiasi oggetto che salva il suo stato con a Bundle.







application-state