mosby: onNewViewStateInstance called every time when returning Fragment from backstack

There is how I put fragment in backstack

    private fun <T : Fragment> putContentFragment(fragment: T): T {
        val fragmentTag = fragment.javaClass.name
        logger.debug("putContentFragment: tag: {}", fragmentTag)

        fragmentMng.beginTransaction()
                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                .replace(R.id.content_frame_layout, fragment, fragmentTag)
                .addToBackStack(fragmentTag)
                .commit()

        return fragment
    }

And there is how I navigateBack to the previous Fragment:

    override fun navigateBack() {
        if (fragmentMng.backStackEntryCount > 1) {
            callback.forceBackPressed() // it calls super.onBackPressed() for MainActivity
        } else {
            callback.finish() // it calls finish() for MainActivity
        }
    }

I also call setRetainInstance(true) on my Fragment. Class variables are restored as they must. I test it via incrementing int var every onViewCreated():

    private var someValue = 0

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        someValue++
        logger.debug("someValue: {}", someValue)
        ...
    }

    override fun createViewState() = NotesViewState()

UPD

Sometimes onNewViewStateInstance() called, sometimes not.

For example, I navigate to new fragment and then return back to the previous: image onNewViewStateInstance called only for new NoteFragment and NotesFragment was restored.

Okay. Lets restart my app and do the same: image

There it is: onNewViewStateInstance called every time when returning Fragment from backstack

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 17 (8 by maintainers)

Most upvoted comments

Yes, this is the desired behavior. Mosby saves the ViewState in the Fragment. Since the Fragment is on the backstack it can not use setRetainInstance(true). Therefore, ViewState has to be Parcelable. Hence return a ViewState implements RestorableParcelableViewState in createViewState()

See documentation: http://hannesdorfmann.com/mosby/viewstate/ section with the name “How does the ViewState survive screen orientation changes?”

Maybe this will be changed in Mosby 3.0.

I had the same issue on my app. What I found was that if onSaveInstanceState was not called (eg. from screen orientation) mosby library could not correctly update the “applyViewState” flag on MvpViewStateInternalDelegate . This flag gets updated in saveViewState and in createOrRestoreViewState. The latter is not called from a retained fragment coming from backstack so the flag is not applied correctly. So I came up with the following delegate to address this issue.

/**
 * Delegate to help an {@link com.hannesdorfmann.mosby.mvp.viewstate.MvpViewStateFragment} retain its viewstate
 * when added to backstack without an orientation change.
 * When adding a fragment to backstack {@link android.support.v4.app.Fragment#onSaveInstanceState(Bundle)}
 * is not called, so the mvp library does not know that a retained viewstate exists. After the first creation we need the mvp
 * library to reset its flags in order to use the retained state with a call in {@link FragmentMvpDelegate#onCreate(Bundle)}.
 *
 * @see MvpViewStateInternalDelegate#applyViewState
 */
public class MvpViewStateBackStackSupportDelegate {


    private boolean mHasBundleOrFirstCreation;

    public MvpViewStateBackStackSupportDelegate() {
        mHasBundleOrFirstCreation = true;
    }

    public void onViewCreated(ViewState viewState, FragmentMvpDelegate mvpDelegate, Bundle savedInstanceState) {
        if (!mHasBundleOrFirstCreation && viewState != null) { //retained view state
            mvpDelegate.onCreate(savedInstanceState); //restore variables to later apply state
        }

        if (mHasBundleOrFirstCreation)
            mHasBundleOrFirstCreation = false; //reset flag, after above check
    }

    public void onSaveInstanceState() {
        mHasBundleOrFirstCreation = true;
    }

Example usage:

    private MvpViewStateBackStackSupportDelegate mBackStackSupportDelegate;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBackStackSupportDelegate = new MvpViewStateBackStackSupportDelegate();
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        setRetainInstance(true);

        mBackStackSupportDelegate.onViewCreated(getViewState(), getMvpDelegate(), savedInstanceState);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mBackStackSupportDelegate.onSaveInstanceState();
    }