1

This question has some similarities to:

Fragment in ViewPager on Fragment doesn't reload on orientation change

However the difference is that I want to know how the fragment states are actually retained as I observe a lot of weird things happening.

My observations are the following:

Setting up a ViewPager with a FragmentPagerAdapter:

  class HomeAdapter extends FragmentPagerAdapter
{
    HomeAdapter(FragmentManager manager)
    {
        super(manager);
    }

    @Override
    public Fragment getItem(int i) {
        try {
            return (Fragment) mTabs.get(i).getFragmentClass().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTabs.get(position).getTitle();
    }

    @Override
    public int getCount() {
        return mTabs.size();
    }
}

In my activity's onCreate I call:

setContentView(R.layout.activity_home);
    mTabs = new ArrayList<>();
    mTabs.add(new SlidingTabItem("A", FragmentA.class));
    mTabs.add(new SlidingTabItem("B", FragmentB.class));

    mViewPager = (ViewPager) findViewById(R.id.viewpager);
    mViewPager.setAdapter(new HomeAdapter(getSupportFragmentManager()));

    mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
    mSlidingTabLayout.setViewPager(mViewPager);

Now when the device orientation changes the viewPager recreates everything down to the changed text in editText fields inside the fragments.

To clarify this is actually the behavior I WANT however I don't understand it.

I checked the source of viewPager and FragmentPagerAdapter. I found out that FragmentPagerAdapter searches the FragmentManager if a Fragment at that position already exists. If it does it calls FragmentTransaction.attach() to reattach it.

It also deattaches the Fragments using FragmentManager.detach()

As far as I understand it this preserves the FragmentState in the FragmentManager. However WHERE and WHEN does this actually get restored.

I tried hooking into each and every lifecycle method in my fragment that contains a savedInstanceState and calling super.X with null as savedInstanceState however the view would still be restored the same way.

The restored fragment also seems to ignore changes to the view done in onCreateView. I tried the following:

mMajor = (EditText) contentView.findViewById(R.id.major);
mMajor.setText("123");

which works the first time the fragment is created. However even though onCreateView is actually called a second time this directive apparently does nothing as the major editText does not reset to "123".

Interestingly calling mMajor.getText().toString() returns something like:

[ 01-31 18:29:54.634 18540:29583 I/ActivityManager ]

which I also don't understand. Is this because the view is not fully created at this point?

So my main question remains. When does the fragment actually recreate its instance and why does it call lifecycle methods even though it does not care about the result and is there any way to detect this without setting an instance variable on first fragment creation?

Blackclaws
  • 436
  • 5
  • 20

1 Answers1

2

When the Fragments are detached by the ViewPager, they'll go through the shutdown lifecycle (onPause(), onSaveInstanceState(), onStop(), onDestroyView(), onDetach(), etc.)

However, ViewPager (more specifically the FragmentStatePagerAdapter) will go through and manually save the fragment's state into its own saved state (calls FragmentManager.saveFragmentInstanceState(fragment) when detaching the fragments). That's likely why your overriding of the methods calling super.*(null) aren't affecting it.

The reason changes to your view are being overridden is because onCreateView() occurs before onViewStateRestored() at which point it will restore the saved state and overwrite your changes. You really shouldn't be setting up your view state in that method anyway -- just return the view.

If you do want to know if it's the first time a fragment is being created, it's sufficient to check if (savedInstanceState == null). If so, it's the first launch. Otherwise, it's recreating itself from saved state.

Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274