title: "Orphaned" fragment vanishes after rotation
body: <p><strong>UPDATE</strong> the issue was that I was using getFragmentManager() througout, instead of using getChildFragmentManager() on fragments with sub-fragments.</p>
  
  <p>The question remains: How would I handle deeply nested fragments in API &lt; 17?</p>
  
  <p><strong>Original Question</strong></p>
  
  <p>My layout structure is as follows.</p>
  
  <p><img src="https://i.stack.imgur.com/1pmU7.png" alt="fragment structure"></p>
  
  <p>Main Activity with layout main.xml has a permanent retained fragment verytop, and in a FrameLayout, either fragment A (a.xml) or fragment B (b.xml) is shown. You can switch between A and B by clicking a button (In abottom_inner there is a button which will bring up B, and in b there is a button that will bring back A again.)</p>
  
  <p>As long as I don't rotate, everything works fine. Also, if I stay with fragment A (don't click the button), and rotate, it also works fine. But if I swich to B, back to A again, and then rotate, abottom_inner is invisible.</p>
  
  <p>This is how it looks and startup (portrait mode, only upper portion shown)</p>
  
  <p><img src="https://i.stack.imgur.com/51Z7h.png" alt="a"></p>
  
  <p>After pressing button "show B":</p>
  
  <p><img src="https://i.stack.imgur.com/f9fxN.png" alt="b"></p>
  
  <p>After pressing "show A", it looks as in first screen shot again. Then, after rotating to landscape, I get this</p>
  
  <p><img src="https://i.stack.imgur.com/tPuFe.png" alt="invisible fragment"></p>
  
  <p>here is the logcat output</p>
  
  <pre><code>***(start)
  Main:          MAIN ONCREATE 
  Main:          adding A (happens only at startup)
  A:             onCreateView
  A:             added atop
  A:             added abottom
  ATop:          onCreateView
  ABottom:       onCreateView
  ABottom:       added aBottomInner
  ABottomInner:  onCreateView
  ABottomInner:  a bottom inner button clicked
  ***(switch to B)
  Main:          replacing A with B
  B:             onCreateView
  B:             b button clicked
  ***(switch back to A)
  Main:          replacing B with A
  A:             onCreateView
  A:             added atop
  A:             added abottom
  ATop:          onCreateView
  ABottom:       onCreateView
  ABottom:       added aBottomInner
  ABottomInner:  onCreateView
  ***(rotate to landscape)
  Main:          MAIN ONCREATE 
  Main:          content exists
  A:             onCreateView
  A:             atop already exists
  A:             abottom already exists
  ABottomInner:  onCreateView
  ABottom:       onCreateView
  ABottom:       aBottomInner already exists
  ATop:          onCreateView
  </code></pre>
  
  <p>Looking at logcat, my guess for the reason of the behaviour is the order in which the onCreateView method is called for each of the child fragments. When it works (at startup, and after button click), the onCreateView of ABottomInner is called AFTER the onCreateView of ABottom. And when it doesn't (after rotate, IF you have clicked the buttons before), the order is reversed. So my guess is that in the case of reversed order, ABottomInner becomes "orphaned" - it relies on ABottom's onCreateView being called before, and if that is not so, it can't attach itself properly. Can anyone confirm or refute my guess? Also, are there any rules about the order in which the onCreateView methods are called during rotate, or is that just random? It would appear so, because if you rotate right after startup, the order is not reversed, and the fragment stays visible.</p>
  
  <p>I have a dirty ugly workaround, which is activated if you check the CheckBox. Then, ABottomInner will be recreated, even if it already exists. Then, it works as it is supposed to. ABottomInner's onCreateView will then be called twice. I can't imagine this being the proper way of doing it. What's the proper way of making sure abottom_inner does not disappear?</p>
  
  <p>Here is complete code.</p>
  
  <p>main.xml</p>
  
  <pre><code>&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:background="@color/white"
      android:gravity="center_horizontal"
      android:orientation="vertical" &gt;
  
      &lt;fragment
          android:id="@+id/VeryTopFragment"
          android:name="com.example.nestedfrags.VeryTop"
          android:layout_width="match_parent"
          android:layout_height="wrap_content" /&gt;
  
      &lt;FrameLayout
          android:id="@+id/contentFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
      /&gt;
  
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>verytop.xml</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="vertical"
      android:background="@color/lime"
  &gt;
  
      &lt;TextView
          android:id="@+id/veryTopTV"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text=" ??? "
          android:layout_gravity="center_horizontal"
      /&gt;
  
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>a.xml</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical" &gt;
  
  
      &lt;FrameLayout
          android:id="@+id/aTop"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:background="@color/orange"
      /&gt;
  
      &lt;FrameLayout
          android:id="@+id/aBottom"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="@color/navy"
      /&gt;
  
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>atop.xml</p>
  
  <pre><code>&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:orientation="vertical"
      android:background="@color/yellow"
  &gt;
  
      &lt;TextView
          android:id="@+id/atopTV"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_gravity="center_horizontal"
          android:text="---"
      /&gt;
  
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>abottom.xml</p>
  
  <pre><code>&lt;FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/iamMain"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:background="@color/silver"&gt;
  
      &lt;CheckBox
          android:id="@+id/checkBox1"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="CheckBox" /&gt;
  
      &lt;FrameLayout
          android:id="@+id/abottomFL"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content" &gt;
      &lt;/FrameLayout&gt;
  
  &lt;/FrameLayout&gt;
  </code></pre>
  
  <p>abottom_inner.xml</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical"
      android:background="@color/white"
  &gt;
  
      &lt;TextView
          android:id="@+id/aBottomInnerTV"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textSize="24sp"
          android:textColor="@color/black"
          android:text=" ??? "
      /&gt;
  
      &lt;Button
          android:id="@+id/aBotInnerButton"
          android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="show B" /&gt;
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>b.xml</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical" &gt;
  
  
      &lt;Button
          android:id="@+id/bButton"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="show A" /&gt;
  
  &lt;/LinearLayout&gt;
  </code></pre>
  
  <p>colors.xml</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt;
  &lt;resources&gt;
      &lt;color name="white"&gt;#FFFFFF&lt;/color&gt;
      &lt;color name="yellow"&gt;#FFFF00&lt;/color&gt;
      &lt;color name="silver"&gt;#C0C0C0&lt;/color&gt;
      &lt;color name="lime"&gt;#00FF00&lt;/color&gt;
      &lt;color name="navy"&gt;#000080&lt;/color&gt;
      &lt;color name="black"&gt;#000000&lt;/color&gt;
      &lt;color name="orange"&gt;#F7931E&lt;/color&gt;
  &lt;/resources&gt;
  </code></pre>
  
  <p>MainActivity.java</p>
  
  <pre><code>package com.example.nestedfrags;
  
  import android.app.Activity;
  import android.app.Fragment;
  import android.app.FragmentManager;
  import android.app.FragmentTransaction;
  import android.os.Bundle;
  import android.util.Log;
  
  public class MainActivity extends Activity {
  
      private static final String TAG = "Main";
  
      public VeryTop veryTop;
  
      Fragment contentFragment;
  
      public void showB(){
          FragmentManager fm = getFragmentManager();
          contentFragment = fm.findFragmentById(R.id.contentFragment);
          FragmentTransaction ft = fm.beginTransaction();
          B b = new B();
          // wouldn't it be nice if android were smart enough to remove "dependents" by itself?!
          ft.remove(fm.findFragmentById(R.id.aTop));
          ft.remove(fm.findFragmentById(R.id.aBottom));
          ft.remove(fm.findFragmentById(R.id.abottomFL));
          if (contentFragment == null){
              Log.i(TAG, "adding B");
              ft.add(R.id.contentFragment, b, "B").commit();
          } else {
              Log.i(TAG, "replacing A with B");
              ft.replace(R.id.contentFragment, b, "B").commit();
          }
      }
      public void showA(){
          FragmentManager fm = getFragmentManager();
          contentFragment = fm.findFragmentById(R.id.contentFragment);
          FragmentTransaction ft = fm.beginTransaction();
          if (contentFragment == null){
              Log.i(TAG, "adding A");
              ft.add(R.id.contentFragment, new A(), "A").commit();
          } else {
              Log.i(TAG, "replacing B with A");
              ft.replace(R.id.contentFragment, new A(), "A").commit();
          }
      }
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          Log.i(TAG, " MAIN ONCREATE ");
          setContentView(R.layout.main);
          FragmentManager fm = getFragmentManager();
          contentFragment = fm.findFragmentById(R.id.contentFragment);
          veryTop = (VeryTop)fm.findFragmentById(R.id.VeryTopFragment);
          if (contentFragment == null){
              Log.i(TAG, "adding A (happens only at startup)");
              FragmentTransaction ft = fm.beginTransaction();
              ft.add(R.id.contentFragment, new A(), "A").commit();
          } else {
              Log.i(TAG, "content exists");
          }
  
      }
  }
  </code></pre>
  
  <p>VeryTop.java</p>
  
  <pre><code>package com.example.nestedfrags;
  
  import android.app.Fragment;
  import android.os.Bundle;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.TextView;
  
  public class VeryTop extends Fragment {
  
      public boolean forceInnerRecreation = false;
  
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setRetainInstance(true);
      }
      public void updateTextView(View v){
          TextView tv = (TextView)v.findViewById(R.id.veryTopTV);
          tv.setText(" very top - forceInnerRecreation is: " + forceInnerRecreation);
      }
      public void setForceInnerRecreation(boolean value){
          forceInnerRecreation = value;
          updateTextView(getView());
      }
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          View v = inflater.inflate(R.layout.verytop, container, false);
          updateTextView(v);
          return v;
      }
  
  }
  </code></pre>
  
  <p>A.java</p>
  
  <pre><code>package com.example.nestedfrags;
  
  import android.app.Fragment;
  import android.app.FragmentManager;
  import android.app.FragmentTransaction;
  import android.os.Bundle;
  import android.util.Log;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  
  public class A extends Fragment {
  
      private static final String TAG = "A";
  
      private void incarnateTop(View v, FragmentManager fm){
          int layoutId = R.id.aTop;
          ATop fragment = (ATop)fm.findFragmentById(layoutId);
          boolean fragmentWasNull = false;
          if (fragment == null){
              fragment = new ATop();
              fragmentWasNull = true;
          }
          fragment.text = " == A TOP == "; 
          if (fragmentWasNull){
              FragmentTransaction ft = fm.beginTransaction();
              ft.add(layoutId, fragment, "atop").commit();
              Log.i(TAG, "added atop");
          } else {
              Log.i(TAG, "atop already exists");
          }
      }
      private void incarnateBottom(View v, FragmentManager fm){
          int layoutId = R.id.aBottom;
          ABottom fragment = (ABottom)fm.findFragmentById(layoutId);
          boolean fragmentWasNull = false;
          if (fragment == null){
              fragment = new ABottom();
              fragmentWasNull = true;
          }
  
          if (fragmentWasNull){
              FragmentTransaction ft = fm.beginTransaction();
              ft.add(layoutId, fragment, "abottom").commit();
              Log.i(TAG, "added abottom");
          } else {
              Log.i(TAG, "abottom already exists");
          }
      }
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          View v = inflater.inflate(R.layout.a, container, false);
          Log.i(TAG, "onCreateView");
          FragmentManager fm = getFragmentManager();
          incarnateTop(v, fm);
          incarnateBottom(v, fm);
          return v;
      }
  
  }
  </code></pre>
  
  <p>ATop.java</p>
  
  <pre><code>package com.example.nestedfrags;
  
  import android.app.Fragment;
  import android.app.FragmentManager;
  import android.os.Bundle;
  import android.util.Log;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.TextView;
  
  public class ATop extends Fragment {
  
      private static final String TAG = "ATop";
  
      String text = "invalid";
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          View v = inflater.inflate(R.layout.atop, container, false);
          Log.i(TAG, "onCreateView");
          FragmentManager fm = getFragmentManager();
          TextView tv = (TextView)v.findViewById(R.id.atopTV);
          tv.setText(text);
          return v;
      }
  
  }
  </code></pre>
  
  <p>ABottom.java</p>
  
  <pre><code>package com.example.nestedfrags;
  
  import android.app.Fragment;
  import android.app.FragmentManager;
  import android.app.FragmentTransaction;
  import android.os.Bundle;
  import android.util.Log;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.CheckBox;
  import android.widget.CompoundButton;
  import android.widget.CompoundButton.OnCheckedChangeListener;
  import android.widget.FrameLayout;
  
  public class ABottom extends Fragment {
  
      private static final String TAG = "ABottom";
  
  
      private void incarnateInner(View v, FragmentManager fm){
          int layoutId = R.id.abottomFL;
          FrameLayout container = (FrameLayout)v.findViewById(layoutId);
          container.setPadding(0, 200, 0, 0);
          ABottomInner fragment = (ABottomInner)fm.findFragmentById(layoutId);
          boolean fragmentWasNull = false;
          if (fragment == null){
              fragment = new ABottomInner();
              fragmentWasNull = true;
          }
          fragment.text = " -- a bottom inner -- ";
          if (fragmentWasNull){
              FragmentTransaction ft = fm.beginTransaction();
              ft.add(layoutId, fragment, "aBottomInner").commit();
              Log.i(TAG, "added aBottomInner");
          } else {
              if (forceInnerRecreation()){
                  FragmentTransaction ft = fm.beginTransaction();
                  fragment = new ABottomInner();
                  fragment.text = " using brute workaround ";
                  ft.replace(layoutId, fragment, "aBottomInner").commit();
                  Log.i(TAG, "putting in fresh copy of aBottomInner");
              } else {
                  Log.i(TAG, "aBottomInner already exists");
              }
          }
      }
      private boolean forceInnerRecreation(){
          MainActivity main = (MainActivity)getActivity();
          return main.veryTop.forceInnerRecreation;
      }
      private void setForceInnerRecreation(boolean value){
          MainActivity main = (MainActivity)getActivity();
          main.veryTop.setForceInnerRecreation(value);
      }
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          View v = inflater.inflate(R.layout.abottom, container, false);
          Log.i(TAG, "onCreateView");
          FragmentManager fm = getFragmentManager();
          CheckBox cb = (CheckBox)v.findViewById(R.id.checkBox1);
          cb.setChecked(forceInnerRecreation());
          OnCheckedChangeListener cbListener = new OnCheckedChangeListener() {
              @Override
              public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                  setForceInnerRecreation(isChecked);
                  if (isChecked){
                      Log.i(TAG, "setting brute workaround ON");
                  } else {
                      Log.i(TAG, "setting brute workaround OFF");
                  }
              }
          };
          cb.setOnCheckedChangeListener(cbListener);
          incarnateInner(v, fm);
          return v;
      }
  
  }
  </code></pre>
  
  <p>ABottomInner.java</p>
  
  <pre><code>package com.example.nestedfrags;
  
  import android.app.Fragment;
  import android.os.Bundle;
  import android.util.Log;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.View.OnClickListener;
  import android.view.ViewGroup;
  import android.widget.Button;
  import android.widget.TextView;
  
  public class ABottomInner extends Fragment {
  
      private static final String TAG = "ABottomInner";
  
      String text = "invalid";
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          View v = inflater.inflate(R.layout.abottom_inner, container, false);
          Log.i(TAG, "onCreateView");
          TextView tv = (TextView)v.findViewById(R.id.aBottomInnerTV);
          tv.setText(text);
          Button btn = (Button)v.findViewById(R.id.aBotInnerButton);
          OnClickListener listener = new OnClickListener() {
              @Override
              public void onClick(View v) {
                  Log.i(TAG, "a bottom inner button clicked");
                  MainActivity main = (MainActivity)getActivity();
                  main.showB();
              }
          };
          btn.setOnClickListener(listener);
          return v;
      }
  }
  </code></pre>
  
  <p>B.java</p>
  
  <pre><code>package com.example.nestedfrags;
  
  import android.app.Fragment;
  import android.os.Bundle;
  import android.util.Log;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.View.OnClickListener;
  import android.widget.Button;
  
  public class B extends Fragment {
  
      private static final String TAG = "B";
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          Log.i(TAG, "onCreateView");
          View v = inflater.inflate(R.layout.b, container, false);
          Button bButton = (Button)v.findViewById(R.id.bButton);
          OnClickListener listener = new OnClickListener(){
              @Override
              public void onClick(View v) {
                  Log.i(TAG, "b button clicked");
                  MainActivity main = (MainActivity)getActivity();
                  main.showA();
              }
          };
          bButton.setOnClickListener(listener);
          return v;
      }
  }
  </code></pre>