0

The docs clearly state that "This method is called after onStart() when the activity is being re-initialized from a previously saved state, given here in savedInstanceState". Several StackOverflow questions concur.

However I am finding this is not entirely accurate and can be misleading. onRestoreInstanceState is only called when switching orientation between horizontal-vertical. It is not called whenever onStart is called.

I modified this simple lab exercise to print out the log traces, and to save mCount into the bundle and display it.

onRestoreInstanceState is not called when you press back button to go back to main Activity nor when you press Home button and relaunch.

Even though I see that onSaveInstanceState and onStop was called each time (from the logs), I do not see a corresponding onRestoreInstanceState after onStart. This is unexpected. The docs should clearly state that only if onDestroy is called, then after onStart, will onRestoreInstanceState be called. Are there are any other cases (other than switching orientation) when it is reliably called?

Knowing these use cases is useful for white-box testing.

Update: android docs have since been clarified - see my answer below.

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private int mCount = 0;
    private TextView mShowCount;
    public static final String EXTRA_MESSAGE =
            "com.example.hellotoast.extra.MESSAGE";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mShowCount = (TextView) findViewById(R.id.show_count);
        Log.d("MainActivity", "Hello World");
    }

    public void showToast(View view) {
        Toast toast = Toast.makeText(this, R.string.toast_message,
                Toast.LENGTH_SHORT);
        toast.show();
        Intent intent = new Intent(this, HelloCount.class);
        intent.putExtra(EXTRA_MESSAGE, mCount);
        startActivity(intent);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putInt(EXTRA_MESSAGE, mCount);
        super.onSaveInstanceState(outState);
        Log.d("MainActivity", "onSaveInstanceState. saved mCount = " + mCount);

    }
    @Override
    public void onRestoreInstanceState(Bundle b) {
        super.onRestoreInstanceState(b);
        Log.d("MainActivity", "onRestoreInstanceState");

        if(b != null) {
            mCount = b.getInt(EXTRA_MESSAGE);
            Log.d("MainActivity", "onRestoreInstanceState mCount = "+mCount);
            mShowCount.setText(Integer.toString(mCount));
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d("MainActivity", "onStart");

    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d("MainActivity", "onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("MainActivity", "onDestroy");
    }

    public void countUp(View view) {
        mCount++;
        if (mShowCount != null) {
            mShowCount.setText(Integer.toString(mCount));
        }
    }
}

HelloCount.java

import static com.example.hellotoast.MainActivity.EXTRA_MESSAGE;

public class HelloCount extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("HelloCount", "onCreate");
        setContentView(R.layout.activity_hello_count);
        TextView tv = findViewById(R.id.helloCount);
        int i =  getIntent().getIntExtra(EXTRA_MESSAGE,0);
        tv.setText(String.valueOf(i));
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d("HelloCount", "onStart");

    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d("HelloCount", "onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("HelloCount", "onDestroy");
    }
}

activity_main.xml

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.hellotoast.MainActivity"
    android:orientation="vertical">

    <Button
        android:id="@+id/button_toast"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:background="@color/colorPrimary"
        android:onClick="showToast"
        android:text="@string/button_label_toast"
        android:textColor="@android:color/white" />


    <TextView
        android:id="@+id/show_count"

        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginBottom="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:background="#FFFF00"
        android:gravity="center_vertical"
        android:text="@string/count_initial_value"
        android:textAlignment="center"
        android:textColor="@color/colorPrimary"
        android:textSize="120sp"
        android:textStyle="bold"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/button_toast"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        />

    <Button
        android:id="@+id/button_count"
        android:layout_below="@+id/show_count"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:background="@color/colorPrimary"
        android:onClick="countUp"
        android:text="@string/button_label_count"
        android:textColor="@android:color/white" />
</RelativeLayout>

activity_second.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="156dp"
        android:layout_marginEnd="8dp"
        android:text="Hello"
        android:textColor="#8BC34A"
        android:textColorHighlight="#00882A2A"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/helloCount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:text="TextView"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        app:layout_constraintVertical_bias="0.099" />

</android.support.constraint.ConstraintLayout>

Click Hello button to launch HelloCount Activity. pressing count button increments counter HelloCount

likejudo
  • 3,396
  • 6
  • 52
  • 107
  • Ummm... what is your question? You are correct that `onRestoreInstanceState()` is called as part of a configuration change, not for every `onStart()` invocation. – CommonsWare Jun 23 '19 at 20:01
  • @CommonsWare the docs as I quoted above, do not say so: "`is called as part of a configuration change, not for every onStart() invocation`". It is misleading. That is also why so many have asked on SO, why it is not being called after onStart – likejudo Jun 23 '19 at 20:48
  • But, that is not a question. Stack Overflow is for questions. If your objective is to put another answer on Stack Overflow related to this topic, either answer the question that you linked to, or convert what you wrote above into an answer to some question. – CommonsWare Jun 23 '19 at 21:04
  • @CommonsWare Even though I see that onSaveInstanceState and onStop was called each time (from the logs), I do not see a corresponding onRestoreInstanceState after onStart. This is unexpected. The docs should clearly state that only if onDestroy is called, then after onStart, will onRestoreInstanceState be called. I am trying to bring attention to this. Also trying to know if there are any other cases (other than switching orientation) when it is called. – likejudo Jun 23 '19 at 23:02
  • 1
    "I am trying to bring attention to this" -- then ask a question and [answer your own question](https://stackoverflow.com/help/self-answer). "Also trying to know if there are any other cases (other than switching orientation) when it is called" -- then ask a question for that specific concern. – CommonsWare Jun 23 '19 at 23:05
  • edited question: Is onRestoreInstanceState only called when switching orientation horizontal-vertical - any other cases? – likejudo Jun 23 '19 at 23:06
  • @CommonsWare I modified my question, unfortunately someone has voted to close it. Moderators seem to be in a great hurry to close questions, IMHO. – likejudo Jun 23 '19 at 23:12
  • 1
    @CommonsWare Android team has updated the docs to clarify: `This method is called between onStart() and onPostCreate(Bundle). This method is called only when recreating an activity; the method isn't invoked if onStart() is called for any other reason.` https://developer.android.com/reference/android/app/Activity.html#onRestoreInstanceState(android.os.Bundle) – likejudo Sep 30 '19 at 17:46

3 Answers3

2

It is called during orientation changes, but also when your activity gets ejected from memory while still being in background.

Your cases are:

  1. When you press home your activity becomes paused (onStop). When you come back to it you receive onStart call and there's no need to restore instance.

  2. When you press back you quit your app, you receive onDestroy call with isFinishing() returning true. When you "come back" there's nothing to restore instance to, new activity is created.

  3. When you pause activity (like point 1.) and keep it in background for a while (preferably navigating around other apps to fill up memory) system might decide to free up your activity from memory. This will cause onSaveInstanceState and onDestroy calls, and when you come back you will get onCreate onRestoreInstanceState.

You can go into developer settings and enable "don't keep activities" option, this will simulate low memory environment and force activities to get destroyed quickly after putting them in background.

Pawel
  • 15,548
  • 3
  • 36
  • 36
  • [Pawel] "2. When you press back you quit your app, you receive onDestroy call... new activity is created.". **Not true in this case.** Pressing Back from HelloCount, takes you back to MainActivity and even though onStart is called now, onRestoreInstanceState is not. – likejudo Jun 23 '19 at 20:52
  • 1
    @likejudo I meant pressing back inside `MainActivity`, sorry if that wasn't clear. – Pawel Jun 23 '19 at 21:02
2

The docs clearly state that "This method is called after onStart() when the activity is being re-initialized from a previously saved state, given here in savedInstanceState". Several StackOverflow questions concur.

However I am finding this is not entirely accurate and can be misleading. onRestoreInstanceState is only called when switching orientation between horizontal-vertical. It is not called whenever onStart is called.

The docs do not state that onRestoreInstanceState is called whenever onStart is called. It clearly states, as you quoted yourself, that it is in the event of activity being re-initialized.

I modified this simple lab exercise to print out the log traces, and to save mCount into the bundle and display it.

onRestoreInstanceState is not called when you press back button to go back to main Activity nor when you press Home button and relaunch.

Neither case causes the activity in question to be killed, so restoring state is unecessary.

Even though I see that onSaveInstanceState and onStop was called each time (from the logs), I do not see a corresponding onRestoreInstanceState after onStart. This is unexpected.

This is completely expected. onSaveInstanceState gives you the opportunity to save your state when your activity is moved to the background in case it gets killed. There is no way to know in advance if it will be killed, so the method is always called, just in case. However, the same is not true of onRestoreInstanceState. In that case, the system knows if you are relaunching the same activity (so no state restoration is needed, so the method is not called) or if you need to restore from state (so the method is called).

The docs should clearly state that only if onDestroy is called, then after onStart, will onRestoreInstanceState be called.

Why? What difference does it make?

Are there are any other cases (other than switching orientation) when it is reliably called?

Why does it matter? You should not care about every single case in which this method will be called. You should only care about implementing it correctly to restore your activity's state. Let the system call it as it deems necessary and your app should work correctly regardless of why it was called.

Community
  • 1
  • 1
dominicoder
  • 9,338
  • 1
  • 26
  • 32
  • "Why? What difference does it make?" I disagree. Without this caveat, one expects it to be called each time after onStart. If it was so clear and obvious to all, there wouldn't be so many questions on SO... "why onRestoreInstanceState wasn't called here?". – likejudo Jun 24 '19 at 15:39
  • My point is, what difference does it make to you coding your app? It's a hook into the Android system that you need to handle if it gets invoked on you. _When_ this happens should be transparent and irrelevant to you. It may be good to know the details for your own learning, but - practically speaking - it makes no difference to how you code your app. – dominicoder Jun 24 '19 at 19:18
  • It definitely makes a difference to how I test my app. e.g. if android is in the dashboard of my car. It is a bit hard to flip the car onto its side and then back on its wheels. – likejudo Jun 24 '19 at 22:09
  • If you're testing saving and restoring state by loading your app into a car and flipping the car to trigger an orientation change you've got bigger problems than incomplete documentation ... – dominicoder Jun 25 '19 at 04:26
-1

I read that onRestoreInstanceState is definitely called when:

  1. horizontal-vertical orientation change.

  2. language input change.

  3. change to multi-windows (whatever that means).

Knowing these use cases is useful for white box testing.

Update:

https://issuetracker.google.com/issues/135968242

Android team has updated the docs to clarify:

This method is called between onStart() and onPostCreate(Bundle). This method is called only when recreating an activity; the method isn't invoked if onStart() is called for any other reason.

likejudo
  • 3,396
  • 6
  • 52
  • 107