Fragment onCreateView and onActivityCreated called twice

I have had the same problem with a simple Activity carrying only one fragment (which would get replaced sometimes). I then realized I use onSaveInstanceState only in the fragment (and onCreateView to check for savedInstanceState), not in the activity.

On device turn the activity containing the fragments gets restarted and onCreated is called. There I did attach the required fragment (which is correct on the first start).

On the device turn Android first re-created the fragment that was visible and then called onCreate of the containing activity where my fragment was attached, thus replacing the original visible one.

To avoid that I simply changed my activity to check for savedInstanceState:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

if (savedInstanceState != null) {
/**making sure you are not attaching the fragments again as they have 
 been 
 *already added
 **/
 return; 
 }
 else{
  // following code to attach fragment initially
 }

 }

I did not even Overwrite onSaveInstanceState of the activity.


Ok, Here's what I found out.

What I didn't understand is that all fragments that are attached to an activity when a config change happens (phone rotates) are recreated and added back to the activity. (which makes sense)

What was happening in the TabListener constructor was the tab was detached if it was found and attached to the activity. See below:

mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
    if (mFragment != null && !mFragment.isDetached()) {
        Log.d(TAG, "constructor: detaching fragment " + mTag);
        FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
        ft.detach(mFragment);
        ft.commit();
    }

Later in the activity onCreate the previously selected tab was selected from the saved instance state. See below:

if (savedInstanceState != null) {
    bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
    Log.d(TAG, "FragmentTabs.onCreate tab: " + savedInstanceState.getInt("tab"));
    Log.d(TAG, "FragmentTabs.onCreate number: " + savedInstanceState.getInt("number"));
}

When the tab was selected it would be reattached in the onTabSelected callback.

public void onTabSelected(Tab tab, FragmentTransaction ft) {
    if (mFragment == null) {
        mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
        Log.d(TAG, "onTabSelected adding fragment " + mTag);
        ft.add(android.R.id.content, mFragment, mTag);
    } else {
        Log.d(TAG, "onTabSelected attaching fragment " + mTag);
        ft.attach(mFragment);
    }
}

The fragment being attached is the second call to the onCreateView and onActivityCreated methods. (The first being when the system is recreating the acitivity and all attached fragments) The first time the onSavedInstanceState Bundle would have saved data but not the second time.

The solution is to not detach the fragment in the TabListener constructor, just leave it attached. (You still need to find it in the FragmentManager by it's tag) Also, in the onTabSelected method I check to see if the fragment is detached before I attach it. Something like this:

public void onTabSelected(Tab tab, FragmentTransaction ft) {
            if (mFragment == null) {
                mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
                Log.d(TAG, "onTabSelected adding fragment " + mTag);
                ft.add(android.R.id.content, mFragment, mTag);
            } else {

                if(mFragment.isDetached()) {
                    Log.d(TAG, "onTabSelected attaching fragment " + mTag);
                    ft.attach(mFragment);
                } else {
                    Log.d(TAG, "onTabSelected fragment already attached " + mTag);
                }
            }
        }

I was scratching my head about this for a while too, and since Dave's explanation is a little hard to understand I'll post my (apparently working) code:

private class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public TabListener(Activity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
        mFragment=mActivity.getFragmentManager().findFragmentByTag(mTag);
    }

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        if (mFragment == null) {
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.replace(android.R.id.content, mFragment, mTag);
        } else {
            if (mFragment.isDetached()) {
                ft.attach(mFragment);
            }
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            ft.detach(mFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
    }
}

As you can see it's pretty much like the Android sample, apart from not detaching in the constructor, and using replace instead of add.

After much headscratching and trial-and-error I found that finding the fragment in the constructor seems to make the double onCreateView problem magically go away (I assume it just ends up being null for onTabSelected when called through the ActionBar.setSelectedNavigationItem() path when saving/restoring state).