简体   繁体   中英

Saving fragment state in Activity

I'm getting the below error when I try to save the state of my fragments in my main activity:

java.lang.IllegalStateException: Fragment MarketviewFragment{c5e0f0d} is not currently in the FragmentManager
    at androidx.fragment.app.FragmentManagerImpl.putFragment(FragmentManager.java:923)
    at com.shoob.capstone.android.crypfolio.MainActivity.onSaveInstanceState(MainActivity.java:308)
    at android.app.Activity.performSaveInstanceState(Activity.java:1608)
    at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1443)
    at android.app.ActivityThread.callActivityOnSaveInstanceState(ActivityThread.java:5038)
    at android.app.ActivityThread.callActivityOnStop(ActivityThread.java:4372)
    at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:4986)
    at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4922)
    at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:69)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1926)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:6986)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)

In my main activity, I have 4 possible fragments that get displayed only one at a time. I keep a reference to the fragments in the main activity and display them as needed:

private MarketviewFragment marketviewFragment;
private WatchlistFragment watchlistFragment;
private PortfolioFragment portfolioFragment;
private DetailsFragment detailsFragment;

I set the current displayed fragment with the below code (only one fragment displayed at a time):

//fragment is an instance of one of the 4 types of fragments above
private void setFragment(Fragment fragment) {
    FragmentManager fm = getSupportFragmentManager();

    if (fm.findFragmentById(R.id.frag_main) != null) {
        fm.beginTransaction()
                .replace(R.id.frag_main, fragment)
                .commit();
    } else {
        fm.beginTransaction()
                .add(R.id.frag_main, fragment)
                .commit();
    }
}

This is how I set each fragment (one example, same for the other types):

private void setPortfolioFragment() {
    if (portfolioFragment == null) {
        portfolioFragment = PortfolioFragment.newInstance();
    }
    setFragment(portfolioFragment);
}

Now for my question. How do I save the state of all these fragments? I want to save them all in a Bundle so I can restore them on recreation of the Main Activity (like on rotation).

The way I'm doing it now doesn't seem to work, what is the correct way to do this?

Saving state:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    if (marketviewFragment != null) {
        getSupportFragmentManager().putFragment(outState, KEY_BUNDLE_MARKETVIEW_FRAGMENT, marketviewFragment);
    }
    if (watchlistFragment != null) {
        getSupportFragmentManager().putFragment(outState, KEY_BUNDLE_WATCHLIST_FRAGMENT, watchlistFragment);
    }
    if (portfolioFragment != null) {
        getSupportFragmentManager().putFragment(outState, KEY_BUNDLE_PORTFOLIO_FRAGMENT, portfolioFragment);
    }
    if (detailsFragment != null) {
        getSupportFragmentManager().putFragment(outState, KEY_BUNDLE_DETAILS_FRAGMENT, detailsFragment);
    }

}

Restoring state:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (savedInstanceState != null) {
        marketviewFragment = (MarketviewFragment)getSupportFragmentManager().getFragment(savedInstanceState, KEY_BUNDLE_MARKETVIEW_FRAGMENT);
        watchlistFragment = (WatchlistFragment)getSupportFragmentManager().getFragment(savedInstanceState, KEY_BUNDLE_WATCHLIST_FRAGMENT);
        portfolioFragment = (PortfolioFragment)getSupportFragmentManager().getFragment(savedInstanceState, KEY_BUNDLE_PORTFOLIO_FRAGMENT);
        detailsFragment = (DetailsFragment)getSupportFragmentManager().getFragment(savedInstanceState, KEY_BUNDLE_DETAILS_FRAGMENT);

    }
}

You should save it every time you detach fragment. also please use detach/attach instead of replace on fragmentManager

another thing is that FragmentManager should restore fragments on it's own. Personally I would do it with FragmentPagerAdapter and just switch visible one.

Best and easiest way would be to have all fragments attached to FragmentManager, and just control visibility. Thanks to that saving/restoring will be done automatically

Your activity should not be handling saving state for each fragment. You should let each fragment handle storing its own state by overriding onSaveInstanceState() in each fragment class:

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    // save fragment state here
}

Also, keeping hard references to all your fragments in your activity is not a great idea.

Edit: After looking at your question again, I understand more what you're trying to do. You're trying to save and restore the actual instances of your fragments. The Android OS will do this automatically for you and you're trying to get in the way of this process. Again, the fact that you are storing the fragments as instance variables is part of the problem and is making you do something you normally wouldn't need to do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM