1

I'm having a little trouble understanding the behavior of fragments inside an activity. Consider the following scenario: I have a holder activity and 2 or more fragments inside. The onCreate method for the activity is like this:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_holder);
        if (savedInstanceState == null) {
            getFragmentManager().beginTransaction().add(R.id.container, new Frag1(), "ZZZ").commit();
        }
    }

I have a button in Frag1 which is linked to a callBack in the activity:

@Override
    public void bam(String s) {
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
        beginTransaction.replace(R.id.container, new Frag2());
        beginTransaction.addToBackStack(null);
        beginTransaction.commit();
    }

At this point Frag2 is on the stack and the only visible Fragment. I used replace and addToBackStack because I need the back navigation. My problem is that when I rotate the screen while inside Frag2, the super.onCreate(savedInstanceState) method from the activity calls the constructor for Frag1.

Is there any way to avoid the call to Frag1's constructor until the user presses the back button?

andreid
  • 225
  • 4
  • 13

2 Answers2

0

You can set properties for activity in manifest file so that your activity wont get destroyed on configuration change like as below :

<activity
        android:name=".HomeActivity"
        android:configChanges="keyboardHidden|orientation|screenSize"
        android:launchMode="singleTask"
        android:windowSoftInputMode="adjustPan|adjustResize" >
    </activity>

android:configChanges="keyboardHidden|orientation|screenSize" ; these are the properties.

Or you can do a cross check, by matching tags of fragment, while adding or replacing fragments.For this you need to code as mention below :

1) Adding tag while adding fragment :

    getFragmentManager().beginTransaction().add(R.id.container, new Frag1(), "TAG NAME").commit();

2) Then check for existing fragment in onCreate() of activity as below :

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_holder);
    fragment1 = getSupportFragmentManager().findFragmentByTag("TAG NAME");
    if(fragment1 == null) { //if fragment null, then add fragment        
        getFragmentManager().beginTransaction().add(R.id.container, new Frag1(), "TAG NAME").commit();
    }
}
mark
  • 503
  • 5
  • 27
  • I know about the settings in the manifest file but I want my activity to be recreated on configuration changes. The second part doesn't work either(I had already tried it). The Frag1 constructor isn't called when adding the transaction getFragmentManager().beginTransaction().add(R.id.container, new Frag1(), "ZZZ").commit(), but on the call to super.onCreate(savedInstanceState); – andreid Nov 12 '14 at 10:25
  • Have you created constructor of your fragment class OR you mean from fragment constructor here is onCreateView() of fragment. – mark Nov 12 '14 at 11:08
  • In the "protected void onCreate(Bundle savedInstanceState) {}" procedure from the activity class the first call (super.onCreate(savedInstanceState);) internally calls Frag1's constructor. So even when the current fragment has been replaced with Frag2, the Frag1 constructor gets called. The onCreateView() from Frag1 is called correctly when Frag1 becomes visible. – andreid Nov 12 '14 at 14:58
0

Fragments added to the backstack stay in memory and cannnot be garbage collected. They are kept as actual references to fragments. The reason it is recreated is because you still have an instance of the fragment. You can still call it's methods and fields as you can with any other object; it's simply not visible to the user and trying to manipulate its views may fail.

If the only purpose of adding the fragment to the backstack is navigation, this can be accomplished by not putting the fragment in the backstack to beging with, thus letting that instance of the fragment fall out of memory, then by overriding the onBackPressed() in the activity you can re-create() your fragment 1. You are free to cache any data you need as well.

The purpose of the backstack is to preserve the fragments state. When it's written to the backstack onDestroyView() is called, but it's viewHierarchy is saved with onSaveInstancestate(). This saves stuff like text in TextViews, scroll positions, etc.

If there's resource intensive stuff in Fragment 1's initialization you can also try moving it to a later lifecycle event, like onResume().

NameSpace
  • 10,009
  • 3
  • 39
  • 40
  • If you look closely you'll see that Frag1 isn't added to the backstack - getFragmentManager().beginTransaction().add(R.id.container, new Frag1(), "ZZZ").commit(). Only Frag2 is added to the backstack. You are right, I could move my initialization code to onResume but I only want to do this as a last option. – andreid Nov 13 '14 at 08:55
  • Even though you are adding a Frag 2, the call to add to backstack adds a fragment 1 to the backstack, because that's what is there. It is putting the transaction on the backstack, so it will have a reference to Fragment 1, since the transaction is 2 replacing 1. If Frag 1 was not in the backstack, and not attached to your activity, it would not be recreated; Even if you had otherwise had a reference to it still, it would behave as any other object -- it's fragment code would essentially be dormant during activity destroy/create cycles. – NameSpace Nov 13 '14 at 10:19