139

I'm trying to save and restore the state of an Activity using the methods onSaveInstanceState() and onRestoreInstanceState().

The problem is that it never enters the onRestoreInstanceState() method. Can anyone explain to me why this is?

biegleux
  • 12,934
  • 11
  • 42
  • 52
BlaBRA
  • 2,121
  • 4
  • 17
  • 13

13 Answers13

192

Usually you restore your state in onCreate(). It is possible to restore it in onRestoreInstanceState() as well, but not very common. (onRestoreInstanceState() is called after onStart(), whereas onCreate() is called before onStart().

Use the put methods to store values in onSaveInstanceState():

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

And restore the values in onCreate():

public void onCreate(Bundle icicle) {
  if (icicle != null){
    value = icicle.getLong("param");
  }
}
Marian Paździoch
  • 7,671
  • 9
  • 49
  • 88
Robert
  • 6,331
  • 4
  • 28
  • 38
  • 2
    the problem is that I use startActivity to return to activity A. When returning to activity B, the object is null icicle. – BlaBRA Nov 05 '10 at 12:32
  • 5
    If I understand correctly, this is what you are doing: From B you call startActivity(A). Then from A you call finish() to get back to B. Right? In that case Your first activity, B will not have been destroyed, and neither onCreate() nor onRestoreInstanceState() will be called. These methods are only called when needed, that is when an activity has been destroyed and needs to be recreated by the system. – Robert Nov 05 '10 at 13:57
  • 4
    I should add that your first activity, B, might get destroyed due to low memory conditions. This will trigger the onCreate and onRestoreInstanceState. – Robert Nov 05 '10 at 13:59
  • @Robert does that mean, the state of B should still be available after A? – erikbwork Aug 03 '11 at 09:22
  • 1
    erikb, yes, activity B will be resumed, or in case the OS has reclaimed it, recreated and then resumed. – Robert Aug 09 '11 at 21:09
  • Thanks for clearing this up. Very helpful. I do have one question though. If `onRestoreInstanceState` doesn't need to be used, when should we be using it? – Andy Jul 19 '12 at 07:54
  • Since the default onRestoreInstanceState restores all views that can be restored, one might want to override it if there is one particular view that for some weird reason shouldn't be restored. – Robert Jul 20 '12 at 05:48
  • Just a correction: "super.onSaveInstanceState(outState);" shoud come first before any NVP entries, else they get destroyed/not saved – Nitin Bansal Aug 07 '12 at 14:41
  • Indeed. Silly me. Thank you, Nitin Bansal. I have corrected the code. – Robert Aug 07 '12 at 15:24
  • thanks for the post but this has me confused I hope it is just a typo but should this line super.onSaveInstanceState(outState); read this super.onSaveInstanceState(icicle); – Gurnard Sep 05 '12 at 11:54
  • Oops, yet another typo. You are absolutely right, Gurnard. I have corrected the code. – Robert Sep 05 '12 at 12:14
  • Just curious ... why is the 'Bundle' variable name "icicle"? – Al Lelopath Jun 19 '15 at 16:51
  • 1
    [Oh, this is why](http://stackoverflow.com/questions/919153/what-is-androids-icicle-parameter) – Al Lelopath Jun 19 '15 at 17:20
  • Actually android docs, says super() should come last, after any entries. Does it matter? – JohnyTex Jun 21 '16 at 08:08
  • JohnyTex, the android source contains many implementations where super() is called first, so I would say that it doesn't matter. But if the documentation says that super() should be called last, it would be prudent to do so. – Robert Jun 30 '16 at 10:21
151

onRestoreInstanceState() is called only when recreating activity after it was killed by the OS. Such situation happen when:

  • orientation of the device changes (your activity is destroyed and recreated).
  • there is another activity in front of yours and at some point the OS kills your activity in order to free memory (for example). Next time when you start your activity onRestoreInstanceState() will be called.

In contrast: if you are in your activity and you hit Back button on the device, your activity is finish()ed (i.e. think of it as exiting desktop application) and next time you start your app it is started "fresh", i.e. without saved state because you intentionally exited it when you hit Back.

Other source of confusion is that when an app loses focus to another app onSaveInstanceState() is called but when you navigate back to your app onRestoreInstanceState() may not be called. This is the case described in the original question, i.e. if your activity was NOT killed during the period when other activity was in front onRestoreInstanceState() will NOT be called because your activity is pretty much "alive".

All in all, as stated in the documentation for onRestoreInstanceState():

Most implementations will simply use onCreate(Bundle) to restore their state, but it is sometimes convenient to do it here after all of the initialization has been done or to allow subclasses to decide whether to use your default implementation. The default implementation of this method performs a restore of any view state that had previously been frozen by onSaveInstanceState(Bundle).

As I read it: There is no reason to override onRestoreInstanceState() unless you are subclassing Activity and it is expected that someone will subclass your subclass.

Ognyan
  • 12,916
  • 4
  • 61
  • 73
  • 3
    yeh this seems to be right, but it sucks. imo it should be also run when returning to the activity from another activity. there are plenty of situations where you need this. – omni Jan 28 '12 at 17:48
  • 4
    @masi there are already other methods invoked on Activity when the user returns to it( from another activity). The onSave/RestoreInstanceState() is used for another specific purpose, that's it. – superjos Nov 16 '12 at 11:09
8

The state you save at onSaveInstanceState() is later available at onCreate() method invocation. So use onCreate (and its Bundle parameter) to restore state of your activity.

Konstantin Burov
  • 67,168
  • 16
  • 112
  • 93
4

As a workaround, you could store a bundle with the data you want to maintain in the Intent you use to start activity A.

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

Activity A would have to pass this back to Activity B. You would retrieve the intent in Activity B's onCreate method.

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

Another idea is to create a repository class to store activity state and have each of your activities reference that class (possible using a singleton structure.) Though, doing so is probably more trouble than it's worth.

sotrh
  • 366
  • 3
  • 11
3

The main thing is that if you don't store in onSaveInstanceState() then onRestoreInstanceState() will not be called. This is the main difference between restoreInstanceState() and onCreate(). Make sure you really store something. Most likely this is your problem.

Charles Caldwell
  • 15,009
  • 4
  • 37
  • 47
3

I found that onSaveInstanceState is always called when another Activity comes to the foreground. And so is onStop.

However, onRestoreInstanceState was called only when onCreate and onStart were also called. And, onCreate and onStart were NOT always called.

So it seems like Android doesn't always delete the state information even if the Activity moves to the background. However, it calls the lifecycle methods to save state just to be safe. Thus, if the state is not deleted, then Android doesn't call the lifecycle methods to restore state as they are not needed.

Figure 2 describes this.

buzz
  • 156
  • 2
  • 6
2

I think this thread was quite old. I just mention another case, that onSaveInstanceState() will also be called, is when you call Activity.moveTaskToBack(boolean nonRootActivity).

Marcos Vasconcelos
  • 17,773
  • 29
  • 104
  • 165
macio.Jun
  • 8,823
  • 1
  • 42
  • 40
1

If you are handling activity's orientation changes with android:configChanges="orientation|screenSize" and onConfigurationChanged(Configuration newConfig), onRestoreInstanceState() will not be called.

Rajkiran
  • 14,425
  • 24
  • 69
  • 108
1

It is not necessary that onRestoreInstanceState will always be called after onSaveInstanceState.

Note that : onRestoreInstanceState will always be called, when activity is rotated (when orientation is not handled) or open your activity and then open other apps so that your activity instance is cleared from memory by OS.

1

From the documentation Restore activity UI state using saved instance state it is stated as:

Instead of restoring the state during onCreate() you may choose to implement onRestoreInstanceState(), which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null:

enter image description here

enter image description here

IMO, this is more clear way than checking this at onCreate, and better fits with single responsiblity principle.

Teoman shipahi
  • 43,086
  • 13
  • 113
  • 137
0

In my case, onRestoreInstanceState was called when the activity was reconstructed after changing the device orientation. onCreate(Bundle) was called first, but the bundle didn't have the key/values I set with onSaveInstanceState(Bundle).

Right after, onRestoreInstanceState(Bundle) was called with a bundle that had the correct key/values.

Jeroen Vannevel
  • 41,258
  • 21
  • 92
  • 157
elvitucho
  • 106
  • 6
0

I just ran into this and was noticing that the documentation had my answer:

"This function will never be called with a null state."

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

In my case, I was wondering why the onRestoreInstanceState wasn't being called on initial instantiation. This also means that if you don't store anything, it'll not be called when you go to reconstruct your view.

Ben Scannell
  • 164
  • 5
0

I can do like that (sorry it's c# not java but it's not a problem...) :

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

AND IN YOUR ACTIVITY FOR RESULT

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

FINALLY

protected override void OnActivityResult(int iRequestCode, Android.App.Result oResultCode, Intent oIntent)
{
    base.OnActivityResult (iRequestCode, oResultCode, oIntent);
    iValue = oIntent.Extras.GetInt("MYVALUE", -1); //=> 1234567890
}
TornaxO7
  • 381
  • 4
  • 14