android - life - onsaveinstancestate example
Enregistrement de l'état d'activité Android à l'aide de l'option Enregistrer l'état de l'instance (18)
Je travaille sur la plate-forme Android SDK et il est difficile de savoir comment enregistrer l'état d'une application. Donc, compte tenu de cette réorganisation mineure de l'exemple '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);
}
}
Je pensais que cela suffirait dans le cas le plus simple, mais il répond toujours avec le premier message, quelle que soit la façon dont je navigue loin de l'application.
Je suis sûr que la solution est aussi simple que de onPause
ou quelque chose du genre, mais cela fait 30 minutes que je onPause
dans la documentation et je n’ai rien trouvé d’évident.
Ajout de LiveData (composants d'architecture Android) à votre projet
ajouter la dépendance suivante
implementation "android.arch.lifecycle:extensions:1.1.0"
LiveData prend un observateur et l'informe des modifications de données uniquement lorsqu'il est dans l'état STARTED ou RESUMED. L'avantage avec LiveData est que lorsque votre activité va dans un État autre que COMMENCÉ ou REPRENDRE il ne sera pas appeler la onChanged méthode de l' observateur .
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);
}
});
}
Ceci est un «gotcha» classique du développement Android. Il y a deux problèmes ici:
- Il existe un bogue subtil dans Android Framework qui complique grandement la gestion de la pile d'applications pendant le développement, du moins sur les versions héritées (je ne suis pas tout à fait sûr si / quand / comment il a été corrigé). Je vais discuter de ce bug ci-dessous.
- Le moyen "normal" ou prévu de gérer ce problème est, en soi, assez compliqué avec la dualité onPause / onResume et onSaveInstanceState / onRestoreInstanceState
En parcourant toutes ces discussions, je soupçonne que la plupart du temps, les développeurs parlent simultanément de ces deux problèmes différents ... d’où toute la confusion et les rapports selon lesquels "cela ne fonctionne pas pour moi".
Premièrement, pour clarifier le comportement «prévu»: onSaveInstance et onRestoreInstance sont fragiles et uniquement pour un état transitoire. L’utilisation prévue (afaict) consiste à gérer la création d’activités lors de la rotation du téléphone (changement d’orientation). En d'autres termes, l'utilisation prévue est lorsque votre activité est toujours logiquement «au-dessus», mais doit toujours être réinitialisée par le système. Le paquet sauvegardé n'est pas conservé en dehors du processus / mémoire / gc, vous ne pouvez donc pas vraiment vous fier à cela si votre activité passe en arrière-plan. Oui, peut-être que la mémoire de votre activité survivra à son voyage en arrière-plan et échappera au GC, mais cela n’est pas fiable (ni prévisible).
Ainsi, si vous avez un scénario où il existe une «progression de l'utilisateur» significative ou un état qui devrait être conservé entre les «lancements» de votre application, il est conseillé d'utiliser onPause et onResume. Vous devez choisir et préparer vous-même un magasin persistant.
MAIS - il y a un bug très déroutant qui complique tout cela. Les détails sont ici:
http://code.google.com/p/android/issues/detail?id=2373
http://code.google.com/p/android/issues/detail?id=5277
Fondamentalement, si votre application est lancée avec l'indicateur SingleTask et que vous la lancez ensuite à partir de l'écran d'accueil ou du menu du lanceur, cette invocation ultérieure créera une NOUVELLE tâche ... vous aurez effectivement deux instances différentes de votre application. habitant la même pile ... ce qui devient très étrange très vite. Cela semble se produire lorsque vous lancez votre application pendant le développement (c.-à-d. À partir d'Eclipse ou d'Intellij). Les développeurs doivent donc souvent faire face à cette situation. Mais aussi par le biais de certains mécanismes de mise à jour de l'App Store (cela impacte également vos utilisateurs).
Je me suis battu dans ces discussions pendant des heures avant de réaliser que mon problème principal était ce bogue, et non le comportement du framework souhaité. Un excellent article et solution de contournement (UPDATE: voir ci-dessous) semble provenir de l'utilisateur @kaciula dans cette réponse:
Comportement de la touche Home
UPDATE Juin 2013 : Quelques mois plus tard, j'ai finalement trouvé la solution "correcte". Vous n'avez pas besoin de gérer vous-même les drapeaux startedApp stateful, vous pouvez le détecter à partir du framework et le libérer correctement. J'utilise ceci près du début de mon 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;
}
}
Les deux méthodes sont utiles et valides et conviennent parfaitement à différents scénarios:
- L'utilisateur met fin à l'application et la rouvre ultérieurement, mais l'application doit recharger les données de la dernière session. Cela nécessite une approche de stockage persistant, telle que l'utilisation de SQLite.
- L'utilisateur change d'application, puis revient à l'original et souhaite reprendre là où il a été laissé - la sauvegarde et la restauration des données d'ensemble (telles que les données d'état de l'application) dans
onSaveInstanceState()
etonRestoreInstanceState()
sont généralement suffisantes.
Si vous enregistrez les données d'état de manière persistante, elles peuvent être rechargées dans onResume()
ou onCreate()
(ou en réalité lors de tout appel de cycle de vie). Ce comportement peut ou peut ne pas être souhaité. Si vous le stockez dans un paquet dans un InstanceState
, il est transitoire et ne convient que pour stocker des données à utiliser dans la même "session" d'utilisateur (j'utilise le terme session de manière vague) mais pas entre "sessions".
Ce n’est pas qu’une approche soit meilleure que l’autre, comme tout, il est simplement important de comprendre le comportement que vous souhaitez et de choisir l’approche la plus appropriée.
Mon collègue a écrit un article expliquant l’état de l’application sur les appareils Android, y compris des explications sur le cycle de vie de l’activité et les informations sur l’état, la SharedPreferences
informations sur l’état, l’enregistrement dans un SharedPreferences
état et des SharedPreferences
.
L'article couvre trois approches:
Stocker des données de contrôle varible / UI locales pendant la durée de vie de l'application (c.-à-d. Temporairement) à l'aide d'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);
}
Stocker des données de contrôle varible / UI locales entre les instances d’application (c’est-à-dire de manière permanente) à l’aide des préférences partagées
[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();
}
Conserver des instances d'objet actives en mémoire entre les activités de la durée de vie de l'application à l'aide d'une instance non configurée conservée
[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();
}
Sauver l'état est au mieux un bécasse en ce qui me concerne. Si vous devez enregistrer des données persistantes, utilisez simplement une SQLite données SQLite . Android rend SOOO facile.
Quelque chose comme ça:
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) {
}
}
}
Un simple appel après ça
dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;
Vous devez remplacer onSaveInstanceState(Bundle savedInstanceState)
et écrire les valeurs d'état de l'application que vous souhaitez modifier comme onSaveInstanceState(Bundle savedInstanceState)
pour le paramètre Bundle
:
@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.
}
Le Bundle est essentiellement un moyen de stocker une carte NVP ("paire paire nom-valeur"). Elle sera transmise à onCreate()
et à onRestoreInstanceState()
où vous extrayez les valeurs comme ceci:
@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");
}
Vous utiliseriez généralement cette technique pour stocker des valeurs d'instance pour votre application (sélections, texte non enregistré, etc.).
onSaveInstanceState
est appelé lorsque le système a besoin de mémoire et tue une application. Il n'est pas appelé lorsque l'utilisateur ferme simplement l'application. Je pense donc que l’état de l’application devrait également être sauvegardé dans onPause
Il devrait être sauvegardé dans un stockage persistant tel que Preferences
ou Sqlite
savedInstanceState
sert uniquement à enregistrer l'état associé à une instance actuelle d'une activité, par exemple les informations de navigation ou de sélection en cours, de sorte que si Android détruit et recrée une activité, elle peut revenir telle qu'elle était auparavant. Voir la documentation sur onCreate
et onSaveInstanceState
Pour un état plus long, envisagez d'utiliser une base de données SQLite, un fichier ou des préférences. Voir Enregistrement d'un état persistant .
Code Kotlin:
enregistrer:
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState.apply {
putInt("intKey", 1)
putString("stringKey", "String Value")
putParcelable("parcelableKey", parcelableObject)
})
}
puis dans onCreate()
ouonRestoreInstanceState()
val restoredInt = savedInstanceState?.getInt("intKey") ?: 1 //default int
val restoredString = savedInstanceState?.getString("stringKey") ?: "default string"
val restoredParcelable = savedInstanceState?.getParcelable<ParcelableClass>("parcelableKey") ?: ParcelableClass() //default parcelable
Ajouter des valeurs par défaut si vous ne voulez pas avoir d'options
En attendant, en général, je n’utilise plus
Bundle savedInstanceState & Co
le cycle de vie est pour la plupart des activités trop compliqué et pas nécessaire. Et Google se dit, ce n'est même pas fiable.
Mon moyen est d’enregistrer immédiatement les modifications dans les préférences
SharedPreferences p;
p.edit().put(..).commit()
D'une certaine manière, les préférences partagées fonctionnent de la même manière que les offres groupées. Et naturellement et au début, ces valeurs doivent être définies par préférence.
Dans le cas de données complexes, vous pouvez utiliser Sqlite au lieu d’utiliser vos préférences.
Lors de l'application de ce concept, l'activité continue d'utiliser le dernier état enregistré, qu'il s'agisse d'une ouverture initiale avec redémarrage intermédiaire ou d'une réouverture en raison de la pile arrière.
Lorsqu'une activité est créée, sa méthode onCreate () est appelée.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
savedInstanceState est un objet de la classe Bundle qui est null pour la première fois, mais il contient des valeurs lorsqu'il est recréé. Pour enregistrer l'état de l'activité, vous devez remplacer onSaveInstanceState ().
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("key","Welcome Back")
super.onSaveInstanceState(outState); //save state
}
mettez vos valeurs dans un objet "outState" Bundle tel que outState.putString ("key", "Welcome Back") et enregistrez en appelant super. Lorsque l'activité est détruite, son état est enregistré dans l'objet Bundle et peut être restauré après une recréation dans onCreate () ou onRestoreInstanceState (). Les lots reçus dans onCreate () et onRestoreInstanceState () sont identiques.
@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");
}
}
ou
//restores activity's saved state
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
String restoredMessage=savedInstanceState.getString("key");
}
Bien que la réponse acceptée soit correcte, il existe une méthode plus rapide et plus simple pour enregistrer l’état de l’activité sur Android à l’aide d’une bibliothèque appelée Icepick . Icepick est un processeur d'annotation qui prend en charge tout le code standard utilisé pour la sauvegarde et la restauration de l'état pour vous.
Faire quelque chose comme ça avec 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);
}
}
Est-ce la même chose que faire ceci:
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 fonctionnera avec tout objet qui enregistre son état avec a Bundle
.
Maintenant, Android fournit ViewModels pour enregistrer l'état, vous devriez essayer de l'utiliser à la place de saveInstanceState.
Mon problème était que je n'avais besoin de persistance que pendant la durée de vie de l'application (une exécution unique comprenant le démarrage d'autres sous-activités au sein de la même application et la rotation de l'appareil, etc.). J'ai essayé diverses combinaisons des réponses ci-dessus mais je n'ai pas obtenu ce que je voulais dans toutes les situations. En fin de compte, ce qui a fonctionné pour moi a été d'obtenir une référence à savedInstanceState lors de onCreate:
mySavedInstanceState=savedInstanceState;
et l'utiliser pour obtenir le contenu de ma variable quand j'en ai besoin, comme suit:
if (mySavedInstanceState !=null) {
boolean myVariable = mySavedInstanceState.getBoolean("MyVariable");
}
J'utilise onSaveInstanceState
et onRestoreInstanceState
comme suggéré ci-dessus mais je suppose que je pourrais aussi ou alternativement utiliser ma méthode pour sauvegarder la variable quand elle change (par exemple en utilisant putBoolean
)
Pour aider à réduire le risque, j'utilise ce qui suit interface
et class
à lire / écrire dans un état Bundle
pour sauvegarder l’instance.
Tout d’abord, créez une interface qui sera utilisée pour annoter vos variables d’instance:
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 {
}
Ensuite, créez une classe où la réflexion sera utilisée pour enregistrer des valeurs dans le 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();
}
}
}
Exemple d'utilisation:
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);
}
}
Remarque: ce code a été adapté à partir d'un projet de bibliothèque nommé AndroidAutowire sous licence MIT .
Pour que les données sur l’activité soient stockées onCreate()
, vous devez d’abord enregistrer les données dans savedInstanceState en surchargeant la SaveInstanceState(Bundle savedInstanceState)
méthode.
Lorsque la SaveInstanceState(Bundle savedInstanceState)
méthode de destruction d'activité est appelée et que vous enregistrez les données que vous souhaitez sauvegarder. Et vous obtenez la même chose onCreate()
quand l'activité redémarre.
Simple rapide pour résoudre ce problème utilise Icepick
Tout d’abord, installez la bibliothèque dans app/build.gradle
repositories {
maven {url "https://clojars.org/repo/"}
}
dependencies {
compile 'frankiesardo:icepick:3.2.0'
provided 'frankiesardo:icepick-processor:3.2.0'
}
Maintenant, regardons cet exemple ci-dessous comment enregistrer l’état dans Activity
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);
}
}
Cela fonctionne pour les activités, les fragments ou tout objet qui doit sérialiser son état sur un paquet (par exemple ViewPresenters du mortier)
Icepick peut également générer le code d'état d'instance pour des vues personnalisées:
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
}
Voici un commentaire de la réponse de Steve Moseley (par ToolmakerSteve ) qui met les choses en perspective (globalement onSaveInstanceState vs onPause, saga East Cost vs West Cost)
@VVK - Je suis partiellement en désaccord. Certains moyens de quitter une application ne déclenchent pas onSaveInstanceState (oSIS). Cela limite l'utilité de oSIS. Cela vaut la peine d'être pris en charge, pour un minimum de ressources du système d'exploitation, mais si une application souhaite remettre l'utilisateur dans l'état où il se trouvait, quelle que soit la façon dont l'application a été sortie, il est nécessaire d'utiliser une approche de stockage persistant. J'utilise onCreate pour vérifier le groupe, et s'il est manquant, vérifiez le stockage persistant. Cela centralise la prise de décision. Je peux récupérer d'un blocage, de la sortie du bouton de retour ou de l'option de menu personnalisée Exit, ou revenir à l'écran de l'utilisateur plusieurs jours plus tard. - ToolmakerSteve 19 sept. 15 à 10:38