android-activity activity - Enregistrement de l'état d'activité Android à l'aide de l'option Enregistrer l'état de l'instance




lifecycle application (23)

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.


Answers

Pour aider à réduire le risque, j'utilise ce qui suit interfaceet classà lire / écrire dans un état Bundlepour 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 .


onSaveInstanceState()pour les données transitoires (restauré dans onCreate()/ onRestoreInstanceState()), onPause()pour les données persistantes (restauré dans onResume()). À partir des ressources techniques Android:

onSaveInstanceState () est appelé par Android si l'activité est en cours d'arrêt et peut être tué avant sa reprise! Cela signifie qu'il devrait stocker tout état nécessaire pour se réinitialiser dans la même condition lorsque l'activité est redémarrée. Il s'agit de la contrepartie de la méthode onCreate () et le bundle savedInstanceState transmis à onCreate () est le même bundle que vous construisez comme outState dans la méthode onSaveInstanceState ().

onPause () et onResume () sont également des méthodes complémentaires. onPause () est toujours appelé à la fin de l'activité, même si nous l'initiions (avec un appel finish () par exemple). Nous allons l'utiliser pour enregistrer la note actuelle dans la base de données. Une bonne pratique consiste à libérer toutes les ressources pouvant être libérées au cours d'une opération onPause (), afin de consommer moins de ressources dans l'état passif.


Pas sûr si ma solution est mal vue ou pas, mais j'utilise un service lié pour conserver l'état ViewModel. Que vous le stockiez en mémoire dans le service ou que vous persistiez et récupériez à partir d'une base de données SqlLite, cela dépend de vos besoins. C’est ce que font les services de toutes les saveurs, ils fournissent des services tels que la gestion de l’état des applications et la logique métier commune abstraite.

En raison des contraintes de mémoire et de traitement inhérentes aux appareils mobiles, je traite les vues Android de la même manière qu'une page Web. La page ne conserve pas l'état, il s'agit uniquement d'un composant de la couche de présentation dont le seul but est de présenter l'état de l'application et d'accepter les entrées de l'utilisateur. Les tendances récentes en matière d’architecture d’applications Web utilisent le modèle MVC (Model, View, Controller), où la page correspond à la vue, les données de domaine au modèle et où le contrôleur repose derrière un service Web. Le même modèle peut être utilisé dans Android avec la vue en cours ... la vue, le modèle correspond aux données de votre domaine et le contrôleur est implémenté en tant que service lié à Android. Chaque fois que vous souhaitez qu'une vue interagisse avec le contrôleur, liez-le au démarrage / reprise et dissociez-le à l'arrêt / la pause.

Cette approche vous donne l’avantage supplémentaire d’appliquer le principe de conception Séparation de préoccupations en ce sens que toute votre logique métier d’application peut être déplacée vers votre service, ce qui réduit la duplication de plusieurs vues et permet à la vue de faire respecter un autre principe de conception important, la responsabilité unique.


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.


Vraiment onSaveInstancedéclarer callen lorsque l'activité passe en arrière-plan

Extrait de la documentation: "la méthode onSaveInstanceState(Bundle)est appelée avant de placer l'activité dans un tel état d'arrière-plan"


Les méthodes onSaveInstanceState(bundle)et onRestoreInstanceState(bundle)sont utiles pour la persistance des données simplement en faisant pivoter l'écran (changement d'orientation).
Ils ne sont même pas bon lors de la commutation entre les applications (car la onSaveInstanceState()méthode est appelée , mais onCreate(bundle)et onRestoreInstanceState(bundle)ne sera relancé.
Pour une utilisation plus persistance des préférences partagées. Lire cet article


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
}

Recréer une activité

Il existe quelques scénarios dans lesquels votre activité est détruite en raison du comportement normal de l'application, par exemple lorsque l'utilisateur appuie sur le bouton Précédent ou que votre activité signale sa propre destruction en appelant finish() . Le système peut également détruire votre activité si elle est actuellement arrêtée et si elle n'a pas été utilisée depuis longtemps ou si l'activité au premier plan nécessite plus de ressources. Le système doit donc arrêter les processus en arrière-plan pour récupérer la mémoire.

Lorsque votre activity est détruite parce que l'utilisateur appuie sur Retour ou que l' activity termine, le concept système de cette instance d' Activity disparu pour toujours car le comportement indique que l'activité n'est plus nécessaire. Cependant, si le système détruit l'activité en raison de contraintes système (plutôt que du comportement normal de l'application), alors, bien que l'instance d'activité ait disparu, le système se souvient qu'elle existait de sorte que si l'utilisateur y accède, il crée une nouvelle instance de l’activité utilisant un ensemble de données sauvegardées décrivant l’état de l’activité lorsqu’elle a été destroyed . Les données enregistrées que le système utilise pour restaurer l'état précédent sont appelées "état d'instance" et constituent un ensemble de paires clé-valeur stockées dans un objet Bundle.

Pour enregistrer des données supplémentaires sur l'état d'activité, vous devez remplacer la méthode de rappel onSaveInstanceState (). Le système appelle cette méthode lorsque l'utilisateur quitte votre activité et lui transmet l'objet Bundle qui sera enregistré dans le cas où votre activité serait détruite de manière inattendue. Si le système doit recréer l'instance d'activité ultérieurement, il passe le même objet Bundle aux onRestoreInstanceState() et onCreate() .

Lorsque le système commence à arrêter votre activité, il appelle onSaveInstanceState() (1) afin que vous puissiez spécifier des données d'état supplémentaires que vous souhaitez enregistrer au cas où l'instance Activity doit être recréée. Si l'activité est détruite et que la même instance doit être recréée, le système transmet les données d'état définies en (1) à la méthode onCreate() (2) et à la méthode onRestoreInstanceState() ).

Enregistrer votre Activity

Lorsque votre activité commence à s'arrêter, le système appelle onSaveInstanceState() afin que votre activité puisse enregistrer des informations d'état avec une collection de paires clé-valeur. L'implémentation par défaut de cette méthode enregistre des informations sur l'état de la hiérarchie de vues de l'activité, telles que le texte dans un widget EditText ou la position de défilement d'un ListView .

Pour enregistrer des informations d'état supplémentaires pour votre activité, vous devez implémenter onSaveInstanceState() et ajouter des paires clé-valeur à l'objet Bundle. Par exemple:

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

Attention: appelez toujours l'implémentation de superclasse de onSaveInstanceState() afin que l'implémentation par défaut puisse enregistrer l'état de la hiérarchie de vues.

Restaurer votre état d' Activity

Lorsque votre activité est recréée après sa destruction, vous pouvez récupérer votre état enregistré à partir de l'ensemble que le système transmet votre activité. Les méthodes de rappel onCreate() et onRestoreInstanceState() reçoivent le même Bundle contenant les informations d'état de l'instance.

Étant donné que la méthode onCreate() est appelée, que le système crée une nouvelle instance de votre activité ou en recrée une précédente, vous devez vérifier si l'état Bundle est null avant de tenter de la lire. Si la valeur est null, le système crée une nouvelle instance de l'activité au lieu de restaurer une instance précédente qui a été détruite.

Par exemple, voici comment vous pouvez restaurer des données d'état dans onCreate() :

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

 }

Au lieu de restaurer l'état pendant onCreate() vous pouvez choisir d'implémenter onRestoreInstanceState() , que le système appelle après la méthode onStart() . Le système appelle onRestoreInstanceState() uniquement s'il y a un état enregistré à restaurer. Vous n'avez donc pas besoin de vérifier si le paquet est null:

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

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.


Notez qu'il n'est pas sûr d'utiliser onSaveInstanceState et onRestoreInstanceState pour des données persistantes , selon la documentation sur les états d'activité dans developer.android.com/reference/android/app/Activity.html .

Le document indique (dans la section 'Cycle de vie de l'activité'):

Notez qu'il est important de sauvegarder les données persistantes dans onPause() au lieu de onSaveInstanceState(Bundle) car ce dernier ne fait pas partie des rappels du cycle de vie. Par conséquent, il ne sera pas appelé dans toutes les situations décrites dans la documentation.

En d'autres termes, placez votre code de sauvegarde / restauration pour les données persistantes dans onPause() et onResume() !

EDIT : Pour plus de précisions, voici la documentation onSaveInstanceState() :

Cette méthode est appelée avant qu'une activité ne soit supprimée, afin de pouvoir restaurer son état ultérieurement. Par exemple, si l'activité B est lancée devant l'activité A et que, éventuellement, l'activité A est supprimée pour récupérer des ressources, l'activité A aura la possibilité de sauvegarder l'état actuel de son interface utilisateur via cette méthode afin que l'utilisateur puisse revenir Pour l'activité A, l'état de l'interface utilisateur peut être restauré via onCreate(Bundle) ou onRestoreInstanceState(Bundle) .


Maintenant, Android fournit ViewModels pour enregistrer l'état, vous devriez essayer de l'utiliser à la place de saveInstanceState.


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


Pour répondre directement à la question initiale. savedInstancestate est null car votre activité n'est jamais recréée.

Votre activité ne sera recréée avec un lot d’états que:

  • Les modifications de configuration, telles que la modification de l'orientation ou de la langue du téléphone, pouvant nécessiter la création d'une nouvelle instance d'activité.
  • Vous revenez à l'application de l'arrière-plan après que le système d'exploitation a détruit l'activité.

Android détruira les activités en arrière-plan en cas de pression de la mémoire ou après une longue période en arrière-plan.

Lorsque vous testez votre exemple hello world, il existe plusieurs façons de quitter et de revenir à l'activité.

  • Lorsque vous appuyez sur le bouton de retour, l'activité est terminée. La relance de l'application est une toute nouvelle instance. Vous ne reprenez pas du fond du tout.
  • Lorsque vous appuyez sur le bouton d'accueil ou utilisez le sélecteur de tâches, l'activité passe à l'arrière-plan. Lors de la navigation vers l'application, onCreate ne sera appelé que si l'activité doit être détruite.

Dans la plupart des cas, si vous appuyez simplement sur la touche d'accueil puis relancez l'application, l'activité n'aura pas besoin d'être recréée. Il existe déjà en mémoire, donc onCreate () ne sera pas appelé.

Il y a une option sous Paramètres -> Options du développeur appelée "Ne pas garder les activités". Lorsqu'elle est activée, Android détruira toujours les activités et les recréera lorsqu'elles seront en arrière-plan. C’est une excellente option à laisser activée lors du développement car elle simule le pire des scénarios. (Un périphérique à faible mémoire recyclant vos activités tout le temps).

Les autres réponses sont précieuses car elles vous apprennent les bonnes manières de stocker l'état, mais je ne pensais pas qu'elles répondaient vraiment POURQUOI votre code ne fonctionnait pas comme prévu


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

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

}

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;

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 .


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 onSaveInstanceStateet onRestoreInstanceStatecomme 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)


Je pense avoir trouvé la réponse. Laissez-moi raconter ce que j'ai fait avec des mots simples:

Supposons que j’ai deux activités, activité1 et activité2 et que je navigue d’activité1 à activité2 (j’ai effectué quelques travaux dans activité2) puis que je retourne à l’activité 1 en cliquant sur un bouton de l’activité1. Maintenant, à ce stade, je voulais revenir à activity2 et je veux voir mon activité2 dans le même état que la dernière fois que j'ai quitté activity2.

Pour le scénario ci-dessus, ce que j'ai fait est que, dans le manifeste, j'ai apporté des modifications comme celle-ci:

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

Et dans le activity1 sur l'événement de clic de bouton, j'ai fait comme ça:

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

Et dans activity2 sur l'événement de clic sur le bouton, j'ai fait comme ceci:

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

Maintenant, ce qui va arriver, c'est que quels que soient les changements que nous avons apportés à l'activité2 ne seront pas perdus, et nous pourrons voir l'activité2 dans le même état que nous avons quitté précédemment.

Je crois que c'est la réponse et que cela fonctionne bien pour moi. Corrigez-moi si je me trompe.


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


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


Intel a publié les instructions d'installation recommandées pour l'émulateur ICS le 15 mai 2012. Cela a fonctionné pour moi. L'émulateur est maintenant rapide et l'interface utilisateur est lisse.

La première moitié des instructions est suffisamment détaillée. Par conséquent, je suppose que vous avez pu installer la ou les images système Atom Intel x86 à l’aide du gestionnaire de SDK Android, ainsi que de Intel HAXM.

Maintenant, assurez-vous que tout le reste est configuré pour que vous puissiez profiter d'un émulateur très performant:

Et commencez le:

sudo kextload -b com.intel.kext.intelhaxm (mac)

Si HAXM fonctionne correctement, vous pouvez voir ce message lors du lancement de l'émulateur:

HAX fonctionne et l'émulateur fonctionne en mode virt rapide

Sinon, vous pouvez voir cette erreur:

HAX ne fonctionne pas et l'émulateur s'exécute en mode d'émulation:
Impossible d'ouvrir le module hax

  • Utilisez l'émulation GPU. Vous ne pouvez pas utiliser l'option Snapshot lorsque vous utilisez l'émulation GPU au moment de l'écriture. Assurez-vous que l'émulation GPU est définie sur "oui".

  • Définissez la mémoire du périphérique sur 1024 Mo ou plus , mais pas plus que le paramètre Intel HAXM. J'utilise 1024 Mo par périphérique et 2048 pour HAXM.

Vérifiez toujours les paramètres après avoir sauvegardé! L'émulateur est très pointilleux sur ce qu'il vous permet de définir et rétablit les configurations sans vous en informer.

Avec ces paramètres, le clavier du logiciel n'apparaît plus, pas plus que les touches de retour à l'écran, les menus et les touches récentes. Cela semble être une limitation de l'image système ICS Intel x86 actuelle. Vous devrez utiliser les raccourcis clavier .

Sous Mac OS, vous devez maintenir les touches fn + control pour que les touches F1 - F12 fonctionnent. Les pages haut / bas / gauche / droite peuvent être réalisées à l'aide des touches Ctrl + Flèches.





android android-activity application-state