Domanda Salvataggio dello stato di attività Android utilizzando Salva stato istanza


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 sia tanto semplice quanto prioritaria onPause o qualcosa del genere, ma ho cercato nella documentazione per 30 minuti circa e non ho trovato nulla di ovvio.


2234
2017-09-30 04:41


origine


risposte:


Devi scavalcare onSaveInstanceState(Bundle savedInstanceState) e scrivere i valori dello stato dell'applicazione che si desidera modificare in Bundle parametro come questo:

@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 di memorizzare una mappa NVP ("Name-Value Pair"), e verrà passata a onCreate() e anche onRestoreInstanceState() dove estrai 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.).


2271
2017-09-30 06:12



Il savedInstanceState è 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.


375
2017-09-30 05:03



Si noti che lo è NON sicuro da usare onSaveInstanceState e onRestoreInstanceState  per dati persistenti, secondo la documentazione sugli stati di attività in http://developer.android.com/reference/android/app/Activity.html.

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

Si noti che è importante salvare   dati persistenti in onPause() anziché   di onSaveInstanceState(Bundle)   perché il dopo non fa parte del   callback del ciclo di vita, quindi non lo sarà   chiamato in ogni situazione come descritto   nella sua documentazione

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

MODIFICARE: Per ulteriori chiarimenti, ecco il onSaveInstanceState() documentazione:

Questo metodo viene chiamato prima che un'attività possa essere uccisa in modo tale che quando lo fa   torna un po 'di tempo in futuro può ripristinare il suo stato. Per   Ad esempio, se l'attività B viene avviata di fronte all'attività A e ad alcuni   l'attività punto A viene uccisa per recuperare risorse, l'attività A avrà   una possibilità di salvare lo stato attuale della sua interfaccia utente tramite questo   metodo in modo che quando l'utente ritorna all'attività A, lo stato di   l'interfaccia utente può essere ripristinata tramite onCreate(Bundle) o    onRestoreInstanceState(Bundle).


365
2018-05-25 23:22



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 salvare nello stato Bundle e SharedPreferences e dai 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();
}

171
2017-08-27 13:54



Questo è un classico 'gotcha' di sviluppo Android. Ci sono due problemi qui:

  • C'è un sottile bug di Android Framework che complica enormemente la gestione dello stack di applicazioni durante lo sviluppo, almeno su versioni legacy (non del tutto sicuro se / quando / come è stato corretto). Discuterò questo bug qui sotto.
  • Il modo "normale" o inteso per gestire questo problema è, a sua volta, piuttosto complicato con la dualità di onPause / onResume e onSaveInstanceState / onRestoreInstanceState

Sfogliando tutti questi thread, sospetto che la maggior parte delle volte gli sviluppatori stiano parlando di questi due diversi problemi simultaneamente ... quindi tutta la confusione e le segnalazioni di "questo non funziona per me".

Innanzitutto, per chiarire il comportamento "previsto": onSaveInstance e onRestoreInstance sono fragili e solo per lo stato transitorio. L'uso previsto (afaict) è di gestire la ricreazione delle attività quando il telefono viene ruotato (cambio di orientamento). In altre parole, l'utilizzo previsto è quando la tua attività è ancora logicamente "in cima", ma deve ancora essere confermata dal sistema. Il pacchetto salvato non è persistente al di fuori del processo / memoria / gc, quindi non puoi fare affidamento su questo se la tua attività va in secondo piano. Sì, forse la memoria della tua attività sopravviverà al suo viaggio sullo sfondo e sfuggirà a GC, ma questo non è affidabile (né è prevedibile).

Quindi, se si dispone di uno scenario in cui è significativo il 'progresso dell'utente' o lo stato che deve essere mantenuto tra "avvia" dell'applicazione, la guida è di utilizzare onPause e onResume. Devi scegliere e preparare da solo un negozio persistente.

MA - c'è un bug molto confuso che complica tutto questo. I dettagli sono qui:

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

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

Fondamentalmente, se la tua applicazione viene lanciata con il flag SingleTask e successivamente la lanci dalla schermata iniziale o dal menu di avvio, la successiva chiamata creerà un nuovo compito ... avrai effettivamente due diverse istanze della tua app abitare lo stesso stack ... che diventa molto strano molto velocemente. Questo sembra accadere quando avvii la tua app durante lo sviluppo (ad esempio da Eclipse o Intellij), quindi gli sviluppatori si imbattono molto in questo. Ma anche attraverso alcuni meccanismi di aggiornamento del negozio di app (quindi influisce anche sugli utenti).

Ho combattuto attraverso questi thread per ore prima che mi rendessi conto che il mio problema principale era questo bug, non il comportamento del framework previsto. Una bella recensione e soluzione (AGGIORNAMENTO: vedi sotto) sembra essere da utente @kaciula in questa risposta:

Comportamento della tastiera principale

AGGIORNAMENTO Giugno 2013: Mesi dopo, ho finalmente trovato la soluzione 'corretta'. Non è necessario gestire da soli qualsiasi flag di stato avviato, è possibile rilevare questo dal framework e la cauzione in modo appropriato. Lo uso vicino all'inizio del mio 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;
    }
}

126
2017-10-19 23:47



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


70
2018-05-07 00:21



Entrambi i metodi sono utili e validi ed entrambi sono i più adatti per diversi scenari:

  1. L'utente termina l'applicazione e la riapre in un secondo momento, ma l'applicazione deve ricaricare i dati dall'ultima sessione: ciò richiede un approccio di archiviazione persistente come l'utilizzo di SQLite.
  2. L'utente cambia applicazione e quindi torna all'originale e desidera riprendere da dove era stato interrotto: salva e ripristina dati bundle (come dati sullo stato dell'applicazione) in onSaveInstanceState() e onRestoreInstanceState() di solito è adeguato.

Se si salvano i dati di stato in modo persistente, è possibile ricaricarli in un file onResume() o onCreate() (o effettivamente su qualsiasi chiamata del ciclo di vita). Questo può essere o non essere il comportamento desiderato. Se lo memorizzi in un pacchetto in un InstanceState, quindi è transitorio ed è adatto solo per la memorizzazione di dati da utilizzare nella stessa "sessione" dell'utente (utilizzo il termine sessione liberamente) ma non tra "sessioni".

Non è che un approccio sia migliore dell'altro, come tutto, è solo importante capire quale comportamento si richiede e selezionare l'approccio più appropriato.


59
2018-06-27 16:17