J'ai travaillé sur la plate-forme Android SDK, et il n'est pas clair comment enregistrer l'état d'une application. Donc, étant donné ce réoutillage mineur de l'exemple «Bonjour, 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 ce serait suffisant pour le cas le plus simple, mais il répond toujours avec le premier message, peu importe la façon dont je m'éloigne de l'application.
Je suis sûr que la solution est aussi simple que de remplacer onPause
ou quelque chose comme ça, mais je fouille dans la documentation depuis environ 30 minutes et je n'ai rien trouvé d'évident.
Réponses:
Vous devez remplacer
onSaveInstanceState(Bundle savedInstanceState)
et écrire les valeurs d'état d'application que vous souhaitez modifier dans leBundle
paramètre comme ceci:Le bundle est essentiellement un moyen de stocker une carte NVP ("paire nom-valeur"), et il sera transmis à
onCreate()
et égalementonRestoreInstanceState()
où vous extrairez les valeurs d'une activité comme celle-ci:Ou d'un fragment.
Vous utiliseriez généralement cette technique pour stocker des valeurs d'instance pour votre application (sélections, texte non enregistré, etc.).
la source
onSaveInstanceState
pratiquement inutile presque sauf pour le cas de changements d'orientation d'écran. Dans presque tous les autres cas, vous ne pouvez jamais vous y fier et devrez enregistrer manuellement votre état d'interface utilisateur ailleurs. Ou empêcher votre application d'être supprimée en remplaçant le comportement du bouton RETOUR. Je ne comprends pas pourquoi ils l'ont même implémenté comme ça en premier lieu. Totalement peu intuitif. Et vous ne pouvez pas avoir cet ensemble que le système vous donne pour enregistrer des choses, sauf dans cette méthode très particulière.View
s auxquels des ID ont été attribués . À partir desonSaveInstanceState
documents: "L'implémentation par défaut prend en charge la plupart de l'état d'interface utilisateur pour vous en appelantonSaveInstanceState()
chaque vue de la hiérarchie qui a un identifiant et en enregistrant l'identifiant de la vue actuellement concentrée (qui est entièrement restaurée par l'implémentation par défaut deonRestoreInstanceState(Bundle)
) "L'
savedInstanceState
option sert uniquement à enregistrer l'état associé à une instance actuelle d'une activité, par exemple les informations de navigation ou de sélection actuelles, de sorte que si Android détruit et recrée une activité, elle puisse revenir comme avant. Voir la documentation pouronCreate
etonSaveInstanceState
Pour un état plus long, envisagez d'utiliser une base de données SQLite, un fichier ou des préférences. Voir Enregistrement de l'état persistant .
la source
Notez qu'il n'est PAS sûr d'utiliser
onSaveInstanceState
etonRestoreInstanceState
pour les données persistantes , selon la documentation sur les états d'activité dans http://developer.android.com/reference/android/app/Activity.html .Le document indique (dans la section «Cycle de vie des activités»):
En d'autres termes, mettez votre code de sauvegarde / restauration pour les données persistantes dans
onPause()
etonResume()
!EDIT : Pour plus de précisions, voici la
onSaveInstanceState()
documentation:la source
Mon collègue a écrit un article expliquant l' état d'application sur les appareils Android , y compris des explications sur le cycle de vie des activités et des informations d'état, comment stocker les informations d'état et d' économie à l'autre
Bundle
etSharedPreferences
et jeter un oeil à ici .L'article couvre trois approches:
Stocker les données de contrôle des variables / UI locales pour la durée de vie de l'application (c'est-à-dire temporairement) à l'aide d'un ensemble d'états d'instance
Stockez les données de contrôle des variables / UI locales entre les instances d'application (c'est-à-dire en permanence) en utilisant les préférences partagées
Conservation des instances d'objet en mémoire entre les activités pendant la durée de vie de l'application à l'aide d'une instance de non-configuration conservée
la source
Il s'agit d'un «piège» classique du développement Android. Il y a deux problèmes ici:
En parcourant tous ces fils, je soupçonne que la plupart du temps, les développeurs parlent de ces deux problèmes différents simultanément ... d'où toute la confusion et les rapports de "cela ne fonctionne pas pour moi".
Tout d'abord, pour clarifier le comportement «prévu»: onSaveInstance et onRestoreInstance sont fragiles et uniquement pour un état transitoire. L'utilisation prévue (afaict) est de gérer les activités récréatives lorsque le téléphone est tourné (changement d'orientation). En d'autres termes, l'utilisation prévue est lorsque votre activité est toujours logiquement «au sommet», mais doit toujours être confirmée par le système. Le bundle enregistré n'est pas conservé en dehors du processus / mémoire / gc, vous ne pouvez donc pas vraiment vous y fier 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 ce n'est pas fiable (ni prévisible).
Donc, si vous avez un scénario où il y a une «progression utilisateur» significative ou un état qui devrait persister entre les «lancements» de votre application, les conseils sont 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, puis que vous la lancez à 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 rapidement. Cela semble se produire lorsque vous lancez votre application pendant le développement (c'est-à-dire depuis Eclipse ou Intellij), donc les développeurs s'y heurtent beaucoup. Mais aussi à travers certains des mécanismes de mise à jour de l'App Store (cela a donc un impact sur vos utilisateurs également).
J'ai lutté à travers ces threads pendant des heures avant de réaliser que mon problème principal était ce bug, pas le comportement du framework prévu. Un grand résumé et
solution de contournement(MISE À JOUR: voir ci-dessous) semble provenir de l'utilisateur @kaciula dans cette réponse:Comportement de la touche Accueil
MISE À JOUR Juin 2013 : Des mois plus tard, j'ai enfin trouvé la «bonne» solution. Vous n'avez pas besoin de gérer vous-même les indicateurs de démarrage avec état, vous pouvez les détecter à partir du framework et les mettre en liberté sous caution de manière appropriée. J'utilise cela au début de mon LauncherActivity.onCreate:
la source
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 doit également être enregistré dansonPause
Il doit être enregistré dans un stockage persistant commePreferences
ouSqlite
la source
Les deux méthodes sont utiles et valides et les deux conviennent le mieux à différents scénarios:
onSaveInstanceState()
etonRestoreInstanceState()
est généralement adéquat.Si vous enregistrez les données d'état de manière persistante, elles peuvent être rechargées dans un
onResume()
ouonCreate()
(ou en fait sur n'importe quel appel de cycle de vie). Cela peut ou non être un comportement souhaité. Si vous le stockez dans un bundle dans unInstanceState
, il est transitoire et ne convient que pour stocker des données à utiliser dans la même `` session '' utilisateur (j'utilise le terme session de manière lâche) mais pas entre les `` sessions ''.Ce n'est pas qu'une approche est meilleure que l'autre, comme tout, il est juste important de comprendre quel comportement vous avez besoin et de sélectionner l'approche la plus appropriée.
la source
Sauver l'état est un kludge au mieux en ce qui me concerne. Si vous devez enregistrer des données persistantes, utilisez simplement une base de données SQLite . Android rend SOOO facile.
Quelque chose comme ça:
Un simple appel après ça
la source
Je pense avoir trouvé la réponse. Permettez-moi de dire ce que j'ai fait en termes simples:
Supposons que j'ai deux activités, activité1 et activité2 et que je navigue de l'activité1 à l'activité2 (j'ai effectué quelques travaux dans l'activité2) et de nouveau à l'activité 1 en cliquant sur un bouton dans l'activité1. Maintenant, à ce stade, je voulais retourner à l'activité2 et je veux voir mon activité2 dans le même état la dernière fois que j'ai quitté l'activité2.
Pour le scénario ci-dessus, ce que j'ai fait, c'est que dans le manifeste, j'ai apporté des modifications comme ceci:
Et dans l'activité1 sur l'événement de clic de bouton, j'ai fait comme ceci:
Et dans activity2 on button click event j'ai fait comme ceci:
Maintenant, ce qui va arriver, c'est que quelles que soient les modifications que nous avons apportées à l'activité2 ne seront pas perdues, et nous pouvons 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 cela fonctionne très bien pour moi. Corrigez-moi si je me trompe.
la source
onSaveInstanceState()
pour les données transitoires (restaurées dansonCreate()
/onRestoreInstanceState()
),onPause()
pour les données persistantes (restaurées dansonResume()
). À partir des ressources techniques Android:la source
onSaveInstanceState()
Est vraiment appelé lorsque l'activité passe en arrière-plan.Citation de la documentation: "Cette méthode est appelée avant qu'une activité ne soit arrêtée afin que lorsqu'elle revienne dans le futur, elle puisse restaurer son état." La source
la source
Pour aider à réduire le passe-partout, j'utilise ce qui suit
interface
etclass
pour lire / écrire dans unBundle
état d'instance d'enregistrement.Créez d'abord une interface qui sera utilisée pour annoter vos variables d'instance:
Ensuite, créez une classe où la réflexion sera utilisée pour enregistrer les valeurs dans le bundle:
Exemple d'utilisation:
Remarque: Ce code a été adapté à partir d'un projet de bibliothèque nommé AndroidAutowire qui est autorisé sous le licence MIT .
la source
En attendant je ne m'en sers plus en général
Le cycle de vie est pour la plupart des activités trop compliqué et pas nécessaire.
Et Google déclare lui-même, ce n'est même pas fiable.
Ma façon est d'enregistrer immédiatement toutes les modifications dans les préférences:
D'une certaine manière, SharedPreferences fonctionne de la même manière que les bundles. Et naturellement et au début, ces valeurs doivent être lues à partir des préférences.
Dans le cas de données complexes, vous pouvez utiliser SQLite au lieu d'utiliser des préférences.
Lors de l'application de ce concept, l'activité continue simplement d'utiliser le dernier état enregistré, qu'il s'agisse d'une ouverture initiale avec redémarrages entre les deux ou d'une réouverture en raison de la pile arrière.
la source
Pour répondre directement à la question d'origine. savedInstancestate est null car votre activité n'est jamais recréée.
Votre activité ne sera recréée avec un ensemble d'états que lorsque:
Android détruira les activités d'arrière-plan lorsqu'il est sous pression de la mémoire ou après avoir été en arrière-plan pendant une période prolongée.
Lorsque vous testez votre exemple de bonjour, il existe plusieurs façons de quitter et de revenir à l'activité.
Dans la plupart des cas, si vous appuyez simplement sur 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'il est activé, Android détruira toujours les activités et les recréera lorsqu'elles seront en arrière-plan. Il s'agit d'une excellente option pour laisser activé lors du développement, car il simule le pire des cas. (Un dispositif à faible mémoire recyclant tout le temps vos activités).
Les autres réponses sont précieuses dans la mesure où elles vous enseignent les bonnes façons de stocker l'état, mais je ne pense pas qu'elles aient vraiment répondu POURQUOI votre code ne fonctionnait pas comme vous vous y attendiez.
la source
Les méthodes
onSaveInstanceState(bundle)
etonRestoreInstanceState(bundle)
sont utiles pour la persistance des données simplement lors de la rotation de 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 , maisonCreate(bundle)
etonRestoreInstanceState(bundle)
ne sera relancé.Pour une utilisation plus persistance des préférences partagées. Lire cet article
la source
onCreate
etonRestoreInstanceState
ne sont pas appelés car ilsActivity
ne sont pas détruits du tout lorsque vous changez d'application, il n'est donc pas nécessaire de restaurer quoi que ce soit. Android appelleonSaveInstanceState
juste au cas où l'activité serait détruite plus tard (ce qui se produit avec 100% de certitude lors de la rotation de l'écran car la configuration complète de l'appareil a changé et l'activité doit être recréée à partir de zéro).Mon problème était que je n'avais besoin de persistance que pendant la durée de vie de l'application (c'est-à-dire une seule exécution comprenant le démarrage d'autres sous-activités dans la même application et la rotation de l'appareil, etc.). J'ai essayé différentes 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 à la sharedInstanceState pendant onCreate:
et l'utiliser pour obtenir le contenu de ma variable quand j'en ai besoin, le long des lignes de:
J'utilise
onSaveInstanceState
etonRestoreInstanceState
comme suggéré ci-dessus mais je suppose que je pourrais aussi ou alternativement utiliser ma méthode pour enregistrer la variable quand elle change (par exemple en utilisantputBoolean
)la source
Bien que la réponse acceptée soit correcte, il existe une méthode plus rapide et plus simple pour enregistrer l'état d'activité sur Android à l'aide d'une bibliothèque appelée Icepick . Icepick est un processeur d'annotation qui s'occupe de tout le code passe-partout utilisé pour enregistrer et restaurer l'état pour vous.
Faire quelque chose comme ça avec Icepick:
C'est la même chose que de faire ceci:
Icepick fonctionnera avec tout objet qui enregistre son état avec a
Bundle
.la source
Lorsqu'une activité est créée, sa méthode onCreate () est appelée.
savedInstanceState est un objet de la classe Bundle qui est null pour la première fois, mais il contient des valeurs lors de sa recréation. Pour enregistrer l'état de l'activité, vous devez remplacer onSaveInstanceState ().
mettez vos valeurs dans l'objet Bundle "outState" comme outState.putString ("clé", "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 la recréation dans onCreate () ou onRestoreInstanceState (). Les bundles reçus dans onCreate () et onRestoreInstanceState () sont identiques.
ou
la source
Il existe essentiellement deux façons de mettre en œuvre ce changement.
onSaveInstanceState()
etonRestoreInstanceState()
.android:configChanges="orientation|screenSize"
.Je ne recommande vraiment pas d'utiliser la deuxième méthode. Étant donné que dans une de mes expériences, la moitié de l'écran de l'appareil était noir lors de la rotation du portrait au paysage et vice versa.
En utilisant la première méthode mentionnée ci-dessus, nous pouvons conserver les données lorsque l'orientation est modifiée ou tout changement de configuration se produit. Je connais une manière dont vous pouvez stocker n'importe quel type de données à l'intérieur de l'objet d'état saveInstance.
Exemple: considérez un cas si vous souhaitez conserver un objet Json. créer une classe modèle avec des getters et des setters.
Maintenant, dans votre activité dans les méthodes onCreate et onSaveInstanceState, procédez comme suit. Cela ressemblera à ceci:
la source
Voici un commentaire de la réponse de Steve Moseley (par ToolmakerSteve ) qui met les choses en perspective (dans l'ensemble onSaveInstanceState vs onPause, east cost vs west cost saga)
la source
Code Kotlin:
enregistrer:
puis dans
onCreate()
ouonRestoreInstanceState()
Ajoutez des valeurs par défaut si vous ne voulez pas d'options
la source
Pour obtenir les données d'état d'activité stockées dans
onCreate()
, vous devez d'abord enregistrer les données dans savedInstanceState en remplaçant laSaveInstanceState(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 enregistrer. Et vous obtenez la même choseonCreate()
lorsque l'activité redémarre. (SavedInstanceState ne sera pas nul puisque vous y avez enregistré des données avant que l'activité ne soit détruite)la source
Simple et rapide pour résoudre ce problème, utilisez IcePick
Tout d'abord, configurez la bibliothèque dans
app/build.gradle
Maintenant, vérifions cet exemple ci-dessous comment enregistrer l'état dans Activity
Il fonctionne pour les activités, les fragments ou tout autre objet qui doit sérialiser son état sur un bundle (par exemple, les ViewPresenters du mortier)
Icepick peut également générer le code d'état d'instance pour les vues personnalisées:
la source
Je ne sais pas si ma solution est désapprouvée ou non, mais j'utilise un service lié pour conserver l'état ViewModel. Que vous le stockiez en mémoire dans le service ou que vous le conserviez et le récupériez à partir d'une base de données SQLite, cela dépend de vos besoins. C'est ce que font des services de toutes sortes, ils fournissent des services tels que le maintien de l'état de l'application 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, elle est purement un composant de 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 de l'architecture des applications Web utilisent le modèle séculaire Modèle, Vue, Contrôleur (MVC), où la page est la vue, les données de domaine sont le modèle et le contrôleur se trouve derrière un service Web. Le même modèle peut être utilisé dans Android avec la vue étant, eh bien ... la vue, le modèle est vos données de 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-la au démarrage / reprise et dissociez-la à l'arrêt / pause.
Cette approche vous offre l'avantage supplémentaire d'appliquer le principe de conception de la séparation des préoccupations en ce que vous pouvez tous déplacer la logique métier de l'application dans votre service, ce qui réduit la duplication de la logique sur plusieurs vues et permet à la vue d'appliquer un autre principe de conception important, la responsabilité unique.
la source
Kotlin
Vous devez remplacer
onSaveInstanceState
etonRestoreInstanceState
pour stocker et récupérer vos variables que vous souhaitez être persistantesGraphique du cycle de vie
Stocker les variables
Récupérer des variables
la source
Maintenant, Android fournit ViewModels pour enregistrer l'état, vous devriez essayer de l'utiliser au lieu de saveInstanceState.
la source
Il existe un moyen de faire en sorte qu'Android enregistre les états sans implémenter aucune méthode. Ajoutez simplement cette ligne à votre déclaration de manifeste d'activité:
Ça devrait ressembler à ça:
Ici vous pouvez trouver plus d'informations sur cette propriété.
Il est recommandé de laisser Android gérer cela pour vous plutôt que de le gérer manuellement.
la source
Que sauver et quoi ne pas faire?
Vous êtes-vous déjà demandé pourquoi le texte du
EditText
fichier est enregistré automatiquement lors d'un changement d'orientation? Eh bien, cette réponse est pour vous.Lorsqu'une instance d'une activité est détruite et que le système recrée une nouvelle instance (par exemple, un changement de configuration). Il essaie de le recréer à l'aide d'un ensemble de données enregistrées de l'ancien état d'activité ( état d'instance ).
L'état d'instance est une collection de paires clé-valeur stockées dans un
Bundle
objet.EditText
ListView
, etc.Si vous avez besoin d'une autre variable à enregistrer en tant que partie de l'état d'instance, vous devez remplacer
onSavedInstanceState(Bundle savedinstaneState)
méthode.Par exemple,
int currentScore
dans une GameActivityPlus de détails sur onSavedInstanceState (Bundle savedinstaneState) lors de l'enregistrement des données
Lequel choisir pour restaurer l'état de l'activité?
OU
Les deux méthodes obtiennent le même objet Bundle, donc peu importe où vous écrivez votre logique de restauration. La seule différence est que dans la
onCreate(Bundle savedInstanceState)
méthode, vous devrez donner une vérification nulle alors qu'elle n'est pas nécessaire dans ce dernier cas. D'autres réponses ont déjà des extraits de code. Vous pouvez les référer.Plus de détails sur onRestoreInstanceState (Bundle savedinstaneState)
Prime
Le
onSaveInstanceState(Bundle savedInstanceState)
n'est invoqué par le système que lorsque l'utilisateur a l'intention de revenir à l'activité. Par exemple, vous utilisez App X et vous recevez soudainement un appel. Vous passez à l'application appelant et revenez à l'application X. Dans ce cas, leonSaveInstanceState(Bundle savedInstanceState)
méthode sera invoquée.Mais considérez ceci si un utilisateur appuie sur le bouton de retour. Il est supposé que l'utilisateur n'a pas l'intention de revenir à l'activité, dans ce cas,
onSaveInstanceState(Bundle savedInstanceState)
il ne sera pas invoqué par le système. Il est important de considérer tous les scénarios lors de la sauvegarde des données.Liens pertinents:
Démo sur le comportement par défaut
Android Official Documentation .
la source
Maintenant, il est logique de faire 2 façons dans le modèle de vue. si vous souhaitez enregistrer la première en tant qu'instance enregistrée: vous pouvez ajouter un paramètre d'état dans le modèle de vue comme celui-ci https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate#java
ou vous pouvez enregistrer des variables ou des objets dans le modèle de vue, dans ce cas, le modèle de vue tiendra le cycle de vie jusqu'à ce que l'activité soit détruite.
la source
vous pouvez utiliser les touches
Live Data
etView Model
Pour L àifecycle Handel
partir deJetPack
. voir cette référence:https://developer.android.com/topic/libraries/architecture/livedata
la source