13

Is there another way of saving the state of the nested fragment ? Or if we shouldn't do this, why ? Thanks !

02-13 11:42:43.258: E/AndroidRuntime(7167): java.lang.IllegalStateException: Can't retain fragements that are nested in other fragments
02-13 11:42:43.258: E/AndroidRuntime(7167):     at android.support.v4.app.Fragment.setRetainInstance(Fragment.java:742)
Bogdan Zurac
  • 6,348
  • 11
  • 48
  • 96
  • 2
    Huh. I'm not getting an exception like you are. For me, onCreateView in the parent simply gets a new child fragment from findFragmentById. Which means it needs to be re-initialized, which is really slow in the case of a Google MapFragment. :( – aij Feb 21 '15 at 20:55
  • 1
    Please take note that, this is no longer an issues after the fix in https://code.google.com/p/android/issues/detail?id=197271 – Cheok Yan Cheng Jul 04 '16 at 02:43
  • 2
    It is released in support library 24 – Cheok Yan Cheng Jul 04 '16 at 02:52
  • 1
    This is still an issue. – Mitch Oct 04 '19 at 20:48

4 Answers4

9

You can use FragmentManager.saveFragmentInstanceState(Fragment) to retrieve a fragment state. The return value implements Parcelable, so you can put it in a Bundle.

For restoration, you can provide the state after creating the fragment using Fragment.setInitialSavedState(Fragment.SavedState).

nicopico
  • 3,606
  • 1
  • 28
  • 30
  • 1
    Looking at the source code, this does not solve this problem. `setRetainInstance` allows you to retain arbitrary class fields, even non-parcelable, non-serializable fields. This method, however, merely calls `onSaveInstanceState(Bundle)` on your fragment, which means all you can do is save stuff you can put in a Bundle. – mxk Oct 22 '13 at 10:55
  • @Matthias Indeed, but in *most* case, this is exactly what you need. `setRetainInstance` is often used instead of implementing proper state management. – nicopico Oct 22 '13 at 18:29
  • 12
    As far as I've seen, you don't actually need to retain the inner fragments, if you set retain instance true on the parent fragment. Why ? Because it will auto retain the children as well. – Bogdan Zurac Oct 30 '13 at 10:25
  • 1
    @Andrew that's why when trying to `setRetainInstance` inside a nested fragment you will get : `unable to retain the instance of the children of nested fragments` – S.Thiongane Oct 24 '15 at 14:30
8

Since support library 20+ (https://code.google.com/p/android/issues/detail?id=74222), there is a bug with instance recreation for child Fragments, there is a fix for it - http://ideaventure.blogspot.com.au/2014/10/nested-retained-fragment-lost-state.html

Code from the webpage(add this to your parent Fragment) -

private FragmentManager childFragmentManager() {//!!!Use this instead of getFragmentManager, support library from 20+, has a bug that doesn't retain instance of nested fragments!!!!
        if(mRetainedChildFragmentManager == null) {
            mRetainedChildFragmentManager = getChildFragmentManager();
        }
        return mRetainedChildFragmentManager;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        if (mRetainedChildFragmentManager != null) {
            //restore the last retained child fragment manager to the new
            //created fragment
            try {
                Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager");
                childFMField.setAccessible(true);
                childFMField.set(this, mRetainedChildFragmentManager);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);

    }
Mikelis Kaneps
  • 4,576
  • 2
  • 34
  • 48
  • Same as above, don't use reflection please. Or at least prove that this is the only way. – NecipAllef Nov 18 '15 at 09:44
  • 7
    @NecipAllef, same as above - if you are telling something is bad - provide a better solution. And downvote only if you know that it exists. – Luten Nov 18 '15 at 10:24
3

This is no longer a limitation with latest support library, after this AOSP commit.

Below is commit message:

Permit setRetainInstance(true) on nested fragments

Save arbitrarily nested fragments across config changes as nonconfiguration objects. This permits the use of retain-instance child fragments as arbitrary opaque dependencies within other fragments.

hidro
  • 12,333
  • 6
  • 53
  • 53
  • This is mostly true, the new* support library update works on 7.0 and 5.0 and below but on Android 6.0 doesn't work correctly. The strategy of using retain fragments will become more of the norm since on 7.0 you only save 1mb of data via onSaveInstance – HexBlit Jan 28 '17 at 21:24
2

Problem: mChildFrgamentManager is being recreated (https://code.google.com/p/android/issues/detail?id=74222)
Workaround: Retaining mChildFrgamentManager if fragment has setRetainInstance(true):

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    if (getRetainInstance()) {
        if (mRetainedChildFragmentManager != null) {
            try {
                Field childFMField = Fragment.class.getDeclaredField("mChildFragmentManager");
                childFMField.setAccessible(true);
                childFMField.set(this, mRetainedChildFragmentManager);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } else {
            mRetainedChildFragmentManager = getChildFragmentManager();
        }
    }
}

Warning: With this code, setRetainInstace should be called before onAttach.

P.S: This is a bit improved version of @attels answer.

Luten
  • 5,420
  • 4
  • 26
  • 24
  • @akshayrajkore what do mean "before"? It IS an ``onAttach`` method of Fragment. – Luten Oct 27 '15 at 10:03
  • Don't use reflection! – NecipAllef Nov 17 '15 at 10:00
  • 3
    @NecipAllef Why? If you have a workaround without reflection - propose it. If not - remove the downvote please. – Luten Nov 18 '15 at 08:05
  • 1
    @akshayrajkore , I misunderstood your question. setRetainInstance should be called in fragment constructor. – Luten Nov 18 '15 at 08:08
  • 1
    @NecipAllef Why should I use ``Activity`` instead of ``Fragment``? The question is not about what to use, it's about how to fix a problem. – Luten Nov 18 '15 at 08:48
  • Using `Activity` doesn't make sense, sorry I removed my comment. But reflection should not be considered a solution. Provided that there is already an answer, I think you should delete your answer. – NecipAllef Nov 18 '15 at 09:35
  • 2
    @NecipAllef, it IS a solution to problem - it;s a fact, cause it solves the problem. How good this solution is - is another question. As there are no better solutions provided - it's a very bad sence to downvote on working solutions. Especially if you don't even understand a question. – Luten Nov 18 '15 at 10:34