Android Lifecycle management of Fragments within ViewPager and FragmentPagerAdapter
android, android-fragmentactivity, android-fragments, android-viewpager
Solution
It's lot to read, but after reading just introduction and the question and having experience with FragmentStatePagerAdapter, which is similar to FragmentPagerAdapter I can tell you that:
After rotation your adapter will AUTOMAGICALLY attach old fragments. So it seems that although activity creating adapter is being recreated, FragmentManager, which is global and it's instance preserve activity's recreation will detect that new FragmentStatePagerAdapter is combined with the same ViewPager and is asking for the same Fragments and will simply fetch them from Fragment's BackStack.
You as designer of Fragments can notice this behavior by continues invocation of Fragment.onAttach() and Fragment.onDetach(). When onAttach() occurs it's either creation of your Fragment or reusing it after rotation. You should be able to distinguish that Fragment was rotated with use of callback onRestoreRnstanceState().
You will see in your logs many onCreate() and other states logs simultaneously, because FragmentStatePagerAdapter always fetches/creates min 3 Fragments (except if you set that they are only 2 or 1), so also after screen rotation 3 fragments will be reattached from backstack.
I hope that it helped.
Problem
I have been struggling to find out what the correct management of Fragments within a `FragmentActivity` with a `ViewPager` is. Before I go into details, a quick summary of the issue that I am facing is the following: I have a `FragmentActivity` with a `ViewPager`. The `ViewPager` uses a custom, yet very simple `FragmentPagerAdapter`. Each `Fragment` within the `ViewPager` comprises of an `ExpandableListView`. I also have an action bar button called "Refresh". For now, let's assume that the `ViewPager` has only one `Fragment`. The activity is created, and the `Fragment`'s `ExpandableListView` is populated (so far so good). When the Refresh button is clicked, the handling method within the `FragmentActivity` iterates over the list of `Fragments` that are assigned to the FragmentPagerAdapter and calls `refresh()` on each `Fragment` to populate its ListView. However, when the orientation of the device changes (e.g. from portrait to landscape), the Activity is recreated and so are the fragments. Clicking the Refresh button now will iterate over non-initialised `Fragments`. I know that I am being quite vague, especially without sample code, but please bear with me. I have traced the problem and method calls as follows from the start of the application/activity: - `FragmentActivity.onCreate()` - `FragmentActivity.setContentView()` - `FragmentActivity.createPagerFragments()` <-- this creates an ArrayList of Fragments and assignes them to a new FragmentPagerAdapter which is in turn assigned to the ViewPager. - `Fragment.onAttach()` - `Fragment.onCreate()` <-- nothing special here, just calling the super method. - `Fragment.onCreateView()` <-- nothing special here either, just inflating the layout - `Fragment.onActivityCreated()` <-- nothing here either. - << All good, orientation changes here >> - `FragmentActivity.onCreate()` - `Fragment.onAttach()` - `Fragment.onCreate()` - `FragmentActivity.setContentView()` - `FragmentActivity.createPagerFragments()` - `Fragment.onCreateView()` - `Fragment.onActivityCreated()` - << Refresh button clicked >> - `FragmentActivity.refresh()` <-- iterates over the newly created Fragments from #13 (not these by Android!). - << Crash: NullPointerException for mExpandableListView in Fragment. >> So the problem, as I see it, is as follows: When Android re-creates the `FragmentActivity` and its `Views` after a change of screen orientation (calls #9-15 above), it creates new `Fragment` objects with their state restored to what the original ones were. However, these ones appear to be completely managed by the `FragmentManager`, and not by the `FragmentPagerAdapter`. In contrast, when the `FragmentPagerAdapter` is re-created along with the `Fragments` in the activity's `onCreate` method (see call #13) the `Fragments` that get assigned to the adapter never have their `Fragment.onCreate()` or `Fragment.onCreateView()` methods called at all. So when the refresh() method is called (see #17) the method iterates over these `Fragments` that have not been initialised. Therefore, when they try to populate the `ExpandableListView`, the view's instance variable is `NULL`. This is to be expected as the instance variable is only assigned in the `Fragment.onCreateView()` method that never gets called on these `Fragments`. So my question is: how does one properly make re-use of the re-recreated (by Android) `Fragments` after the screen orientation has changed in order to avoid creating new ones that don't get initialised? I need to have a valid reference to them, in order to call their refresh() method that populates them on-demand. Ideally, they should also be assigned to the `FragmentPagerAdapter` as well. I hope I have been clear in describing the issue, and the reason that I have not provided sample code is because the problem (as can be seen) is not from the code itself but from a rather incorrect (seemigly) re-creation of Fragments rather than re-use. But if needed, I can give you sample code, I just through this way would be clearer. Thank you!