Search code examples
androidrotationandroid-fragmentsandroid-lifecycle

Some fragments are lost on rotation


In my FragmentActivity I use several fragments. when configuration changed(on rotation) system destroys and re-creates each fragment.
I try to use this re-created fragments

mFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_frame);

and put it to new layout

getSupportFragmentManager().beginTransaction()
    .replace(R.id.fragment_frame, mFragment )       
    .commit();

but meet with strange behavior: several fragments are lost. they are not displayed on the screen more. in their place is empty.
Although the logs show that fragments are re-creates fine, but further shows that the fragment is again destroyed, again created and destroyed again.

I build test project for show problem more clearly and without husk.
and that demonstrable results (not enough reputation to insert pictures)
Activity started picture
Activity rotated picture

A,B,C,D is a fragments
As you can see after configuration changed some fragments are lost (B,D)

tell me what I'm doing wrong

here's the code to help (Edit:push code to gitHub in order to make it easier to try)

https:// github.com/nailgilaziev/TestFragmentsRetain

p/s sorry about links

here log cat after I rotate device

MainActivity: onPause Activity
MainActivity: onStop Activity
MainActivity: onDestroy Activity
A: onDestroyview
A: onDestroy
B: onDestroyview
B: onDestroy
C: onDestroyview
C: onDestroy
D: onDestroyview
D: onDestroy
MainActivity: onCreate Activity
A: onCreate
B: onCreate
C: onCreate
D: onCreate
MainActivity: replaced
MainActivity: onStart Activity
A: onCreateView
B: onCreateView
C: onCreateView
D: onCreateView

<--before this point all fine and then strange behavior began
for the lost fragments(B,D) are performed unnecessary calls-->

B: onDestroyview
B: onDestroy
D: onDestroyview
D: onDestroy
D: onCreate
D: onCreateView
MainActivity: onResume Activity

Solution

  • By breaking down your problem such that there are two Fragments instead of four, you will see that onCreateView gets called multiple times on the same Fragment when orientation changes. This is an indication that the same Fragment is being added/created more than once.

    Basically, when orientation changes, your Fragments will be re-attached automatically so there is no need to perform a FragmentTransaction every time onCreate(Bundle) is called.

    What you should be doing in onCreate(Bundle) instead is...

    1. Null check the savedInstanceState Bundle that is passed into onCreate(Bundle). -- If an orientation change occurs, onCreate(Bundle) gets invoked again and the savedInstanceState Bundle will become non-null. FragmentActivity saves data in onSaveInstanceState(Bundle) before it is re-created.

    2. If savedInstanceState is null, create your Fragments and attach them via FragmentTransaction. Otherwise, do nothing.

    In summary, your revised code should resemble the following...

    FragmentManager fm = getSupportFragmentManager();
    if(savedInstanceState == null) {
        fm.beginTransaction()
                .replace(R.id.a, new A())
                .replace(R.id.b, new B())
                .replace(R.id.c, new C())
                .replace(R.id.d, new D())
                .commit();
    }
    else {
        a = fm.findFragmentById(R.id.a);
        b = fm.findFragmentById(R.id.b);
        c = fm.findFragmentById(R.id.c);
        d = fm.findFragmentById(R.id.d);
    }
    

    Also, side note, the savedInstanceState Bundle in the onCreate(Bundle) method of a Fragment will always be null, unless you save something to it in onSaveInstanceState(Bundle), so don't assume saveInstanceState will also be non-null in a Fragment when it is re-created, as it is in the case of a FragmentActivity.