onSaveInstanceState () et onRestoreInstanceState ()

138

J'essaie de sauvegarder et de restaurer l'état d'un en Activityutilisant les méthodes onSaveInstanceState()et onRestoreInstanceState().

Le problème est qu'il n'entre jamais dans la onRestoreInstanceState()méthode. Quelqu'un peut-il m'expliquer pourquoi?

BlaBRA
la source
1
@Nitin: merci d'avoir partagé le lien ... cela a éclairci quelques points pour moi +1
Taliadon
2
@NitinBansal le lien est mort.
ashishdhiman2007

Réponses:

191

Habituellement, vous restaurez votre état dans onCreate(). Il est possible de le restaurer onRestoreInstanceState()également, mais ce n'est pas très courant. ( onRestoreInstanceState()s'appelle après onStart(), alors que onCreate()s'appelle avant onStart().

Utilisez les méthodes put pour stocker les valeurs dans onSaveInstanceState():

protected void onSaveInstanceState(Bundle icicle) {
  super.onSaveInstanceState(icicle);
  icicle.putLong("param", value);
}

Et restaurez les valeurs dans onCreate():

public void onCreate(Bundle icicle) {
  if (icicle != null){
    value = icicle.getLong("param");
  }
}
Robert
la source
2
le problème est que j'utilise startActivity pour revenir à l'activité A. Lors du retour à l'activité B, l'objet est nul glaçon.
BlaBRA
5
Si je comprends bien, voici ce que vous faites: à partir de B, vous appelez startActivity (A). Ensuite, à partir de A, vous appelez finish () pour revenir à B. N'est-ce pas? Dans ce cas, votre première activité, B n'aura pas été détruite, et ni onCreate () ni onRestoreInstanceState () ne seront appelés. Ces méthodes ne sont appelées qu'en cas de besoin, c'est-à-dire lorsqu'une activité a été détruite et doit être recréée par le système.
Robert
4
Je dois ajouter que votre première activité, B, pourrait être détruite en raison de conditions de mémoire insuffisantes. Cela déclenchera onCreate et onRestoreInstanceState.
Robert
1
erikb, oui, l'activité B sera reprise, ou au cas où le système d'exploitation l'aurait récupérée, recréée puis reprise.
Robert
1
Oh, c'est pourquoi
Al Lelopath
149

onRestoreInstanceState()est appelée uniquement lors de la recréation d'une activité après sa suppression par le système d'exploitation. Une telle situation se produit lorsque:

  • l'orientation de l'appareil change (votre activité est détruite et recréée).
  • il y a une autre activité devant la vôtre et à un moment donné, le système d'exploitation tue votre activité afin de libérer de la mémoire (par exemple). La prochaine fois que vous démarrerez votre activité, onRestoreInstanceState () sera appelée.

En revanche: si vous êtes dans votre activité et que vous appuyez Backsur le bouton de l'appareil, votre activité est terminée () ed (c'est-à-dire que vous la considérez comme une application de bureau sortante) et la prochaine fois que vous démarrez votre application, elle est lancée "à neuf", c'est-à-dire sans état enregistré parce que vous l'avez quitté intentionnellement lorsque vous avez frappé Back.

Une autre source de confusion est que lorsqu'une application perd le focus sur une autre application onSaveInstanceState()est appelée, mais lorsque vous revenez à votre application, elle onRestoreInstanceState()peut ne pas être appelée. C'est le cas décrit dans la question initiale, c'est-à-dire si votre activité n'a PAS été tuée pendant la période où une autre activité était en avant onRestoreInstanceState(), ne sera PAS appelée car votre activité est quasiment "vivante".

Dans l'ensemble, comme indiqué dans la documentation pour onRestoreInstanceState():

La plupart des implémentations utiliseront simplement onCreate (Bundle) pour restaurer leur état, mais il est parfois pratique de le faire ici une fois que toute l'initialisation a été effectuée ou pour permettre aux sous-classes de décider d'utiliser votre implémentation par défaut. L'implémentation par défaut de cette méthode effectue une restauration de tout état d'affichage précédemment gelé par onSaveInstanceState (Bundle).

Tel que je l'ai lu: Il n'y a aucune raison de passer outre à onRestoreInstanceState()moins que vous ne sous-classiez Activityet que l'on s'attende à ce que quelqu'un sous-classe votre sous-classe.

Ognyan
la source
3
yeh cela semble être juste, mais ça craint. imo il doit également être exécuté lors du retour à l'activité à partir d'une autre activité. vous en avez besoin dans de nombreuses situations.
masi
4
@masi il existe déjà d'autres méthodes invoquées sur Activity lorsque l'utilisateur y revient (depuis une autre activité). OnSave / RestoreInstanceState () est utilisé dans un autre but spécifique, c'est tout.
superjos
8

L'état dans lequel vous enregistrez onSaveInstanceState()est disponible ultérieurement lors de l' onCreate()appel de méthode. Utilisez donc onCreate(et son Bundleparamètre) pour restaurer l'état de votre activité.

Konstantin Burov
la source
4

Pour contourner ce problème, vous pouvez stocker un ensemble avec les données que vous souhaitez conserver dans l'intention que vous utilisez pour démarrer l'activité A.

Intent intent = new Intent(this, ActivityA.class);
intent.putExtra("bundle", theBundledData);
startActivity(intent);

L'activité A devrait le renvoyer à l'activité B. Vous récupéreriez l'intention dans la méthode onCreate de l'activité B.

Intent intent = getIntent();
Bundle intentBundle;
if (intent != null)
    intentBundle = intent.getBundleExtra("bundle");
// Do something with the data.

Une autre idée est de créer une classe de référentiel pour stocker l'état de l'activité et que chacune de vos activités fasse référence à cette classe (possible en utilisant une structure singleton.) Cependant, cela pose probablement plus de problèmes que cela ne vaut la peine.

sotrh
la source
3

La chose principale est que si vous ne stockez pas onSaveInstanceState()alors onRestoreInstanceState()ne sera pas appelé. C'est la principale différence entre restoreInstanceState()et onCreate(). Assurez-vous de vraiment stocker quelque chose. C'est probablement votre problème.

user1771286
la source
1
onRestoreInstanceState () sera appelé, même si vous ne stockez rien dans OnSaveInstanceState ()
abh22ishek
3

J'ai trouvé que onSaveInstanceState est toujours appelé lorsqu'une autre activité vient au premier plan. Et est donc onStop.

Cependant, onRestoreInstanceState était appelé uniquement lorsque onCreate et onStart étaient également appelés. Et, onCreate et onStart n'étaient PAS toujours appelés.

Il semble donc qu'Android ne supprime pas toujours les informations d'état, même si l'activité passe à l'arrière-plan. Cependant, il appelle les méthodes du cycle de vie pour enregistrer l'état juste pour être sûr. Ainsi, si l'état n'est pas supprimé, Android n'appelle pas les méthodes du cycle de vie pour restaurer l'état car elles ne sont pas nécessaires.

La figure 2 décrit cela.

bourdonner
la source
2

Je pense que ce fil était assez ancien. Je viens de mentionner un autre cas, qui onSaveInstanceState()sera également appelé, c'est lorsque vous appelez Activity.moveTaskToBack(boolean nonRootActivity).

macio.Jun
la source
1

Si vous gérez les changements d'orientation de l'activité avec android:configChanges="orientation|screenSize"et onConfigurationChanged(Configuration newConfig), onRestoreInstanceState()ne sera pas appelé.

Rajkiran
la source
1

Il n'est pas nécessaire que onRestoreInstanceState soit toujours appelé après onSaveInstanceState.

Notez que: onRestoreInstanceState sera toujours appelé, lorsque l'activité est pivotée (lorsque l'orientation n'est pas gérée) ou ouvrez votre activité, puis ouvrez d'autres applications afin que votre instance d'activité soit effacée de la mémoire par le système d'exploitation.

Ashutosh Srivastava
la source
1

Dans la documentation, restaurer l'état de l'interface utilisateur de l'activité à l'aide de l'état d'instance enregistré, il est indiqué comme suit:

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 existe un état enregistré à restaurer, vous n'avez donc pas besoin de vérifier si le Bundle est nul :

entrez la description de l'image ici

entrez la description de l'image ici

OMI, c'est un moyen plus clair que de vérifier cela sur onCreate, et correspond mieux au principe de responsabilité unique.

Shipahi Teoman
la source
0

Dans mon cas, a onRestoreInstanceStateété appelé lorsque l'activité a été reconstruite après avoir changé l'orientation de l'appareil. onCreate(Bundle)a été appelé en premier, mais le bundle ne contenait pas les clés / valeurs que j'ai définies onSaveInstanceState(Bundle).

Juste après, a onRestoreInstanceState(Bundle)été appelé avec un bundle qui avait les bonnes clés / valeurs.

elvitucho
la source
0

Je viens de rencontrer ceci et j'ai remarqué que la documentation avait ma réponse:

"Cette fonction ne sera jamais appelée avec un état nul."

https://developer.android.com/reference/android/view/View.html#onRestoreInstanceState(android.os.Parcelable)

Dans mon cas, je me demandais pourquoi onRestoreInstanceState n'était pas appelé lors de l'instanciation initiale. Cela signifie également que si vous ne stockez rien, il ne sera pas appelé lorsque vous reconstruirez votre vue.

Ben Scannell
la source
0

Je peux faire comme ça (désolé c'est c # pas java mais ce n'est pas un problème ...):

private int iValue = 1234567890;

function void MyTest()
{
    Intent oIntent = new Intent (this, typeof(Camera2Activity));
    Bundle oBundle = new Bundle();
    oBundle.PutInt("MYVALUE", iValue); //=> 1234567890
    oIntent.PutExtras (oBundle);
    iRequestCode = 1111;
    StartActivityForResult (oIntent, 1111);
}

ET DANS VOTRE ACTIVITÉ POUR LE RÉSULTAT

private int iValue = 0;

protected override void OnCreate(Bundle bundle)
{
    Bundle oBundle =  Intent.Extras;
    if (oBundle != null)
    {
        iValue = oBundle.GetInt("MYVALUE", 0);
        //=>1234567890
    }
}

private void FinishActivity(bool bResult)
{
    Intent oIntent = new Intent();
    Bundle oBundle = new Bundle();
    oBundle.PutInt("MYVALUE", iValue);//=>1234567890
    oIntent.PutExtras(oBundle);
    if (bResult)
        {
            SetResult (Result.Ok, oIntent);
        }
    else
        SetResult(Result.Canceled, oIntent);
    GC.Collect();
    Finish();
}

ENFIN

protected override void OnActivityResult(int iRequestCode, Android.App.Result oResultCode, Intent oIntent)
{
    base.OnActivityResult (iRequestCode, oResultCode, oIntent);
    iValue = oIntent.Extras.GetInt("MYVALUE", -1); //=> 1234567890
}
EDynamic90
la source