question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

onNewViewStateInstance called every time when returning Fragment from backstack

See original GitHub issue

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

Issue Analytics

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

github_iconTop GitHub Comments

2reactions
sockeqwecommented, Aug 20, 2016

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.

1reaction
fmnstrcommented, Oct 10, 2016

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();
    }
Read more comments on GitHub >

github_iconTop Results From Across the Web

How can I maintain fragment state when added to the back ...
If you return to a fragment from the back stack it does not re-create the fragment but ... The existing FragmentA instance's onCreateView()...
Read more >
Saving state with fragments - Android Developers
To ensure the user's state is saved, the Android framework automatically saves and restores the fragments and the back stack.
Read more >
Creating and Using Fragments | CodePath Android Cliffnotes
onDetach() is called when fragment is no longer connected to the activity. The lifecycle execution order is mapped out below: lifecycle. The most...
Read more >
Fragments backstack working - CommonsWare
But i observed that every time time this query ran, both my constructor & onCreateview() was being called again. I guess This means...
Read more >
You are correct that a Fragment is retrieved from the ... - Medium
You are correct that a Fragment is retrieved from the back stack it may not have its onCreateView called. If the Fragment and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found