Experience porting an Android app to Kotlin

I think it’s pretty true for fragments. Once you’ve pushed a bunch of fragments in the back stack using FragmentTransition.replace(), your fragments’ onDestroyView will be called, however, the fragment instances will be kept in the memory. Next time the fragment gets popped back, onCreateView on the same instance will be called again to create new views. Nulling old views immediately will definitely help free up UI resources (assuming you don’t reference them elsewhere than the fragment itself), especially when you have a large back stack.

ButterKnife looks helpful in this scenario, where you can call ButterKnife.unbind to nullify these views. But using lateinit on fragment views doesn’t seem right to me, as these fields are meant to be nullified later, not just initialised later. Furthermore, using generated Java code to break the ‘non-null contract’ on fields seem more wrong to me.

I do find a cleaner way of doing this: if you have a lot of views in fragment, create an internal class holding all views. By doing this, the fragment just needs to hold a nullable instance of the view holder class. Thanks to Kotlin’s compact syntax, this turns out to be a good outcome:

class MyFragment : Fragment() {
    // The view holder class
    internal class Views(rootView : View,
                         val titleView : View = rootView.findViewById(R.id.title),
                         val contentView : View = rootView.findViewById(R.id.content))

    private var views : Views? = null

    override protected fun onCreateView(....) = inflate(R.layout.fragment).apply {
        // Create views with inflated rootView
        views = Views(this).apply {
            titleView.text = "title"
            contentView.text = "content"
        }
    }

    override protected fun onDestroyView() {
        // Nullify the views object so all views can be freed up.
        views = null
    }

    // Do something with title view
    private fun func1() {
        views?.titleView?.text = "Title has changed"
    }

    // Do something with both titleView and contentView
    private fun func2() {
        views?.apply {
            titleView.text = "title has changed"
            contentView.text = "Content has changed"
        }
    }
}