Home > Back-end >  How setRetainInstance(true) in Fragment know that it has to be retained?
How setRetainInstance(true) in Fragment know that it has to be retained?

Time:10-23

We have a method in Fragment that is used to retain Fragment in cases of orientation change when the Activity is destroyed and re-created again,

setRetainInstance(Boolean true/false)

How does it differentiate between when the Activity is re-created (destroyed and created again) and not fully destroyed?

Basically, I'm wondering how the Fragment knows when it has to be fully destroyed and when it has to be retained.

CodePudding user response:

Quite simply: it doesn't.

The retained Fragments are added to a ViewModel within the FragmentManager. When the Activity is recreated, the ViewModel is restored internally (as part of ComponentActivity) via the Activity's onRetainNonConfigurationInstance()/getLastNonConfigurationInstance() mechanism. If the Activity isn't being recreated, it and its non-configuration instance simply go away, and they're eventually cleaned up by the garbage collector.

CodePudding user response:

Ryan's answer gave you the pointers, but you have to read the source:


    /**
     * Control whether a fragment instance is retained across Activity
     * re-creation (such as from a configuration change). If set, the fragment
     * lifecycle will be slightly different when an activity is recreated:
     * <ul>
     * <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
     * will be, because the fragment is being detached from its current activity).
     * <li> {@link #onCreate(Bundle)} will not be called since the fragment
     * is not being re-created.
     * <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
     * still be called.
     * </ul>
     *
     * @param retain <code>true</code> to retain this fragment instance across configuration
     *               changes, <code>false</code> otherwise.
     *
     * @see #getRetainInstance()
     * @deprecated Instead of retaining the Fragment itself, use a non-retained Fragment and keep
     * retained state in a ViewModel attached to that Fragment. The ViewModel's constructor and
     * its onCleared() callback provide the signal for initial creation and final destruction of
     * the retained state.
     */
    @Deprecated
    public void setRetainInstance(boolean retain) {
        FragmentStrictMode.onSetRetainInstanceUsage(this);
        mRetainInstance = retain;
        if (mFragmentManager != null) {
            if (retain) {
                mFragmentManager.addRetainedFragment(this);
            } else {
                mFragmentManager.removeRetainedFragment(this);
            }
        } else {
            mRetainInstanceChangedWhileDetached = true;
        }
    }

(source: cs.android.com)

In other words, the FragmentManager has a list of retained fragments. So it knows which fragments must be retained.

Needless to say but using setRetainInstance(true) has been deprecated; it is preferred to save whatever state you were trying to retain in a ViewModel that will survive a configuration change (among other things), and restore the state when/if the fragment is recreated.

About "why is this using a ViewModel if I'm not": Well you're free to not use a ViewModel, but the framework itself wouldn't want to pass on such a convenient opportunity to separate the concerns :)

  • Related