how i can break things with Fragments with setRetainInstance(true) and adding them to backstack?

android, android-fragments, android-orientation

Solution

Updated answer:

What are the scenarios where I might get in trouble?

When adding a `Fragment` to the back stack and passing a `Bundle` in the `Fragment` from `onSaveInstanceState()` to `onCreateView()` on configuration change. Calling `setRetainInstance(true)` will set the `Bundle` to null on configuration change.

(I'm not sure a developer would actually attempt this since using `setRetainInstance(true)` makes `onSaveInstanceState()` kind of redundant, but I didn't see the behaviour documented in the API docs so I wrote up this answer).

If both `addToBackStack()` and `setRetainInstance(true)` are called, `setRetainInstance()` partly alters the `Fragment` lifecycle method calls and parameter values on configuration changes, compared to calling only `addToBackStack()`.

Specifically, in the test below, looking a differences between calling only `addToBackStack()` and calling `setRetainInstance(true)` as well, and seeing what happens on configuration change:

Calling `addToBackStack()` but not `setRetainInstance(true)`;

- `onCreate()` and `onDestroy()` are called.

- a bundle passed from `onSaveInstanceState()` is received as a parameter in `onCreateView()`.

Calling both `addToBackStack()` and `setRetainInstance(true)`:

- `onCreate()` and `onDestroy()` are not called. This is metioned in the API docs.

- a bundle passed from `onSaveInstanceState()` is not received in `onCreateView()`. The passed-in `Bundle` is null.

A test with logged method calls and parameters tested for null:

In the `Activity`:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    MyFragment fragment;
    if (savedInstanceState != null) {
        fragment = (MyFragment) getFragmentManager().findFragmentByTag("my_fragment_tag");
    } else {
        fragment = new MyFragment();
        FragmentTransaction t = getFragmentManager().beginTransaction();
        t.addToBackStack(null);//toggle this
        t.add(android.R.id.content, fragment, "my_fragment_tag").commit(); 
    }
}

In the `Fragment`:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setRetainInstance(true);//toggle this
}

and

@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString("test", "value");
    super.onSaveInstanceState(outState);
}

Test 1: Fragment lifecycle when `addToBackStack()` is called , and `setRetainInstance(true)` is not called

- onAttach()

- onCreate()

- onCreateView()

- onActivityCreated()

- onStart()

- onResume()

[Device rotated from portrait to landscape]

- onPause()

- onSaveInstanceState()

- onStop()

- onDestroyView()

- onDestroy()

- onDetach()

- onAttach()

- onCreate()

- onCreateView() with bundle param != null

- onStart()

- onResume()

Test 2 & 3: Fragment lifecycle calls with `setRetainInstance(true)` called, `addToBackStack()` called / not called (same result):

- onAttach()

- onCreateView()

- onActivityCreated()

- onStart()

- onResume()

[Device rotated from portrait to landscape]

- onPause()

- onSaveInstanceState()

- onStop()

- onDestroyView()

- onDetach()

- onAttach()

- onCreateView() with bundle param == null

- onStart()

- onResume()

Problem

the docs on setRetainInstance say : This can only be used with fragments not in the back stack. so I started playing with it. I have one Activity with adds first frag A ``` FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.content, new PackageFragment()); ft.commit ``` then from this frag I run a method from parent Activity which adds frag B to backstack ``` FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.content, new OrderFragment()); ft.addToBackStack(null); ft.commit(); ``` then I create log msg from onCreate,onDestroy,onSaveInstanceState,onActivityCreated...etc I try two versions of this process. Rotating the device on each fragment. - default Everything is as expected. onCreate, onDestroy on fragments fire - setRetainInstance(true) Everything is as expected?. onCreate, onDestroy on fragments dont fire and all seems to work while fragments are in the backstack.. so why the docs say I shouldnt use it? What are the scenarios where I might get in trouble? thanks

Original source