0

I have a MainActivity that manages two Fragments: InputDataFragment (is the one displayed at the opening of the app) which contains a series of EditText and ResultsFragment, which replaces InputDataFragment after a button is clicked.
I want to save the data in the EditText fields so that, even after configuration changes such as screen rotations, if I navigate back from ResultsFragment to InputDataFragment, I can use those data to fill them again.
This is the code I use to navigatefrom InputDataFragment to ResultsFragment:

@Override
public void onCalculate(String firstElement, String secondElement) {

    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    ResultsFragment resultsFragment = new ResultsFragment();
    fragmentTransaction.replace(R.id.container, resultsFragment);
    fragmentTransaction.commit();
}

This is where I save the values in InputDataFragment

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

    // Check if there's something to retain before putting it into outState Bundle
    if (!firstEditText.getText().toString().isEmpty()) {
        outState.putString("firstElement", firstEditText.getText().toString());
        Log.d(LOG_TAG, "Power saved");
    }

    if (!secondEditText.getText().toString().isEmpty()) {
        outState.putString("secondElement", secondEditText.getText().toString());
    }
}

And this is where I retrieve them

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    if (savedInstanceState != null) {
        firstEditText.setText(savedInstanceState.getString("firstElement"));
        secondEditText.setText(savedInstanceState.getString("secondElement"));
    }
}

I've tried doing what was suggested in the accepted answers on these Stack Overflow questions, saving and restoring my Fragment references:

Once for all, how to correctly save instance state of Fragments in back stack?

Using onSaveInstanceState with fragments in backstack?

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

    getSupportFragmentManager().putFragment(outState, "dataFragment", dataFragment);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    if (savedInstanceState != null) {
        dataFragment = (InputDataFragment) getSupportFragmentManager().getFragment(savedInstanceState, "dataFragment");
    } else {
        dataFragment = new InputDataFragment();
    }

    fragmentTransaction.add(R.id.container, dataFragment);
    fragmentTransaction.commit();
}

The values are correctly present in Bundle when onActivityCreated is called in the Fragment because I've tried to Log them

The problem is that I get an error in the line where I call putFragment within onSaveInstanceState, when I'm in ResultFragment and I rotate the device.

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mycompany.myproject, PID: 20161
java.lang.IllegalStateException: Fragment InputDataFragment{679fd26} is not currently in the FragmentManager
    at androidx.fragment.app.FragmentManagerImpl.putFragment(FragmentManager.java:923)
    at com.mycompany.myproject.MainActivity.onSaveInstanceState(MainActivity.java:21)
    at android.app.Activity.performSaveInstanceState(Activity.java:1646)
    at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1508)
    at android.app.ActivityThread.callActivityOnSaveInstanceState(ActivityThread.java:5476)
    at android.app.ActivityThread.callActivityOnStop(ActivityThread.java:4792)
    at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:5424)
    at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:5342)
    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:2155)
    at android.os.Handler.dispatchMessage(Handler.java:109)
    at android.os.Looper.loop(Looper.java:207)
    at android.app.ActivityThread.main(ActivityThread.java:7539)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
Michele
  • 326
  • 8
  • 18
  • If your `EditText` has an `android:id`, then its state is automatically saved and restored. You don't need any `onSaveInstanceState()` code for that. – ianhanniballake Aug 05 '19 at 22:25
  • If your `EditTexts` have ids, then you don't need to manually save them because `super.onSaveInstanceState()` already handles that. You should only override `onSaveInstanceState()` (and call super) to save variables that live in your activity/fragment and you need to save/restore if your activit/fragment is destroyed and recreated. Check https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en – fernandospr Aug 06 '19 at 00:28
  • My `EditTexts` properly save their state when I'm in *InputDataFragment* and rotate the device. But the problem is that if I navigate to *ResultsFragment*, and there I rotate the device, when I go back to *InputDataFragment* the `EditTexts` are empty. As suggested in the link provided by @fernandospr I gave every `EditText` an `android:id` but it still doesn't work – Michele Aug 06 '19 at 08:52
  • Also, my `EditTexts` properly save their state if from *InputDataFragment* I navigate to *ResultsFragment* and back to *InputDataFragment* _without_ rotating the device – Michele Aug 06 '19 at 09:08

1 Answers1

0

I solved it, the problem was here

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

if (savedInstanceState != null) {
    dataFragment = (InputDataFragment) getSupportFragmentManager().getFragment(savedInstanceState, "dataFragment");
} else {
    dataFragment = new InputDataFragment();
}

fragmentTransaction.add(R.id.container, dataFragment);
fragmentTransaction.commit();
}

When I'm in ResultsFragment and rotate the device, Activity onCreate method is called. Even though I got the previous InputDataFragment back from Bundle, calling FragmentTransaction.add() caused the loss of the data.

Moving FragmentTransaction code to the case where savedInstanceState == null was the solution.

Michele
  • 326
  • 8
  • 18