Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fragment instance is retained but child fragment is not re-attached

Update: accepted answer points to explanation (bug) with a work-around, but also see my Kotlin based work-around attached as an answer below.

This code is in Kotlin, but I think it is a basic android fragment life-cycle issue.

I have a Fragment that holds a reference to an other "subfragment"

Here is basically what I am doing:

  1. I have a main fragment that has retainInstance set to true
  2. I have a field in the main fragment that will hold a reference to the subfragment, initially this field is null
  3. In the main fragment's onCreateView, I check to see if the subfragment field is null, if so, I create an instance of the subFragment and assign it to the field
  4. Finally I add the subfragment to a container in the layout of the main fragment.
  5. If the field is not null, ie we are in onCreateView due to a configuration change, I don't re-create the subfragment, I just try to added it to the containter.

When the device is rotated, I do observe the onPaused() and onDestroyView() methods of the subfragment being called, but I don't see any lifecyle methods being called on the subfragment during the process of adding the retained reference to the subfragment, to the child_container when the main fragments view is re-created.

The net affect is that I don't see the subfragment view in the main fragment. If I comment out the if (subfragment == null) and just create a new subfragment everytime, i do see the subfragment in the view.

Update

The answer below does point out a bug, in which the childFragmentManager is not retained on configuration changes. This will ultimately break my intended usage, which was to preserve the backstack after rotation, however I think what I am seeing is something different.

I added code to the activities onWindowFocusChanged method and I see something like this when the app is first launched:

activity is in view
fm = FragmentManager{b13b9b18 in Tab1Fragment{b13b2b98}}
tab 1 fragments = [DefaultSubfragment{b13bb610 #0 id=0x7f0c0078}]

and then after rotation:

activity is in view
fm = FragmentManager{b13f9c30 in Tab1Fragment{b13b2b98}}
tab 1 fragments = null

here fm is the childFragmentManager, and as you can see, we still have the same instance of Tab1Fragment, but it has a new childFragmentManager, which I think is unwanted and due to the bug reported in the answer below. The thing is that I did add the subfragment to this new childFragmentManger. So it seems like the transaction never executes with the reference to the fragment that was retained, but does complete if I create a brand new fragment. (I did try calling executePendingTransactions on the new childFragmentManager)


class Tab1Fragment: Fragment() {

    var subfragment: DefaultSubfragment? = null

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
        val rootView = inflater!!.inflate(R.layout.fragment_main, container, false)
        if (subfragment == null ) {
            subfragment = DefaultSubfragment()
            subfragment!!.sectionLabel = "label 1"
            subfragment!!.buttonText = "button 1"
        }
        addRootContentToContainer(R.id.child_container, content = subfragment!!)

    return rootView
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        retainInstance = true
    }

inline fun Fragment.addRootContentToContainer(containerId: Int, content: Fragment) {
    val transaction = childFragmentManager.beginTransaction()
    transaction.replace(containerId, content)
    transaction.commit()
}
like image 414
nPn Avatar asked Jan 02 '16 18:01

nPn


1 Answers

Your problem looks similar to the issue described here:

https://code.google.com/p/android/issues/detail?id=74222

unfortunately this issue will probably not be fixed by google.

Using retained fragments for UI or nested fragments is not a good idea - they are recomended to be used in place of onRetainNonConfigurationInstance, so ie. for large collections/data structures. Also you could find Loaders better than retained fragments, they also are retained during config changes.

btw. I find retained fragments more of a hack - like using android:configChanges to "fix" problems caused by screen rotations. It all works until user presses home screen and android decides to kill your app process. Once user will like to go back to your app - your retained fragments will be destroyed - and you will still have to recreate it. So its always better to code everything like if your resources could be destroyed any time.

like image 146
marcinj Avatar answered Sep 18 '22 14:09

marcinj