Search code examples
androidandroid-fragmentsandroid-listfragment

What to do with Fragments when they are dismissed (not visible)?


I am rewriting a Bluetooth app with 3 Activities to use just 1 Activity and 3 Fragments:

diagram

So I have now 4 files:

  • MainActivity.java (contains bluetooth and shared preferences code)
  • MainFragment.java (contains ellipsis menu to show SettingsFragment)
  • SettingsFragment.java (contains "scan" button to show ScanningFragment)
  • ScanningFragment.java (displays nearby bluetooth devices in a list)

It almost works, but as an Android programming newbie I don't understand - what to do with Fragments when I show some other Fragment?

Should I just drop the Fragments (and remove from FragmentManager?) to be garbage collected?

Or should I add these 3 private variables to MainActivity.java and reuse them (when the user navigates forwards and backwards)?

private MainFragment mMainFragment;
private SettingsFragment mSettingsFragment;
private ScanningFragment mScanningFragment;

Or does FragmentManager somehow manage all 3 Fragment for me - regardless if they are visible or not?

Here is my current code (it is simple, I just call replace() all the time)-

public class MainActivity extends Activity implements 
                                            MainListener, 
                                            SettingsListener, 
                                            ScanningListener,
                                            BleWrapperUiCallbacks {

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);        
        setContentView(R.layout.activity_main); // empty FrameLayout

        Fragment fragment = new MainFragment();
        getFragmentManager().beginTransaction()
            .replace(R.id.root, fragment, "main")
            .commit();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:
                Fragment fragment = new SettingsFragment();
                getFragmentManager().beginTransaction()
                    .addToBackStack(null)
                    .replace(R.id.root, fragment, "settings")
                    .commit();
                break;
        }

        return super.onOptionsItemSelected(item);
    }

    // implementing SettingsFragment.SettingsListener interface

    public void scanClicked() {
        // TODO how to stop indicator when returned?
        setProgressBarIndeterminateVisibility(true);

        String address = // get from shared preferences
        Fragment fragment = ScanningFragment.newInstance(address);
        getFragmentManager().beginTransaction()
            .addToBackStack(null)
            .replace(R.id.root, fragment, "scan")
            .commit();
    }

Solution

  • Should I just drop the Fragments (and remove from FragmentManager?) to be garbage collected?

    No need to do anything else. FragmentManager is the guy in charge of Fragments' lifecycle. Once you call replace(), FragmentManager takes care for the rest. If needed it will keep fragment in memory, or release it.

    Or should I add these 3 private variables to MainActivity.java and reuse them (when the user navigates forwards and backwards)?

    No, don't do it because of the said above.

    Or does FragmentManager somehow manage all 3 Fragment for me - regardless if they are visible or not?

    Yes, it does. For instance, if you have invisible retained fragment, it's enough to create it once, and FragmentManager will take care of it and will keep it even when activity gets re-created during configuration change.

    If you create fragments dynamically (as far as I can see, this is your case) then I suggest to add very first fragment dynamically too. You can do it like this.

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);        
        setContentView(R.layout.activity_main); // empty FrameLayout
    
        if (savedInstanceState == null) {  // <- important
            Fragment fragment = new MainFragment();
            getFragmentManager().beginTransaction()
                .replace(R.id.root, fragment, "main")
                .commit();
        }
    }
    

    This will ensure you don't duplicate MainFragment on configuration change, because when savedInstanceState is not null, then FragmentManager keeps instance of your fragment already.