120

I'm trying to implement Navigation with Jetpack's architecture components in my existing app.

I have a single activity app where the main fragment (ListFragment) is a list of items. Currently, when the user taps on a list item a second fragment is added to the stack by fragmentTransaction.add(R.id.main, detailFragment). So when back is pressed the DetailFragment is detached and the ListFragment is shown again.

With Navigation architecture this is handled automatically. Instead of adding the new fragment it's replaced, so the fragment view is destroyed, onDestroyView() is called and onCreateView() is called when back is pressed to recreate the view.

I understand that this is a good pattern used with LiveData and ViewModel to avoid using more memory than necessary, but in my case this is annoying because the list has a complex layout and inflating it is time and CPU consuming, also because I'll need to save the scroll position of the list and scroll again to the same position user left the fragment. It's possible but seems it should exists a better way.

I've tried to "save" the view in a private field on fragment and re-use it on onCreateView() if is already there, but it seems an anti-pattern.

private View view = null;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    if (view == null) {
        view = inflater.inflate(R.layout.fragment_list, container, false);
        //...
    }

    return view;
}

Is there any other, more elegant, way to avoid re-inflating the layout?

pauminku
  • 3,526
  • 3
  • 22
  • 24
  • 16
    Did you find an answer for this ? I'm currently stuck in the same situation – Raicky Derwent Feb 12 '19 at 09:42
  • No, it seems there's no other solution than re-create the view. – pauminku Feb 12 '19 at 10:09
  • Just to let other users know, it seems very easy to save and restore scroll position of lists (recyclerView), for exemple look this other question: https://stackoverflow.com/questions/47110168/layoutmanager-onsaveinstancestate-not-working – pauminku Feb 13 '19 at 12:21
  • I managed to fix this issue in a different way in my code. Rather than creating a new viewmodel each time in onCreateView(), I used ViewModelProviders.of(). This will retain my scroll position without having to go through savedInstanceStates – Raicky Derwent Feb 25 '19 at 10:02
  • 1
    Yes, this is the proper way to work with ViewModel, but it doesn't solve the problem of re-inflating the view. I think the pattern is to re-inflate it, prioritizing memory usage than performance. – pauminku Feb 25 '19 at 10:05
  • You can also not use Navigation for those 2 views but do it for others – cutiko Mar 09 '19 at 00:56
  • did you solve this problem ? does Ian Lake solution really work ? – Kapta May 16 '19 at 08:24
  • yes, it works, but take in consideration that consumes more memory as said. – pauminku May 22 '19 at 09:43
  • @pauminku can we please share the working code to this problem. – Akash Chaudhary Apr 16 '20 at 09:03
  • look at my answer on this question maybe will help you [Save state in Navigation Component](https://stackoverflow.com/a/63749092/8747163) – elblasy Sep 04 '20 at 23:44

10 Answers10

67

Ian Lake from google replied me that we can store the view in a variable and instead of inflating a new layout, just return the instance of pre-stored view on onCreateView()

Source: https://twitter.com/ianhlake/status/1103522856535638016

Leakcanary may show this as leak but its false positive..

anticafe
  • 6,816
  • 9
  • 43
  • 74
erluxman
  • 18,155
  • 20
  • 92
  • 126
  • 3
    so, the code example I've putted in the question it's correct? glad to know. Can you please put the link to the conversation with Ian Lake? in case it's on a public forum. – pauminku Mar 07 '19 at 09:25
  • 1
    It would be a great help if we can have a sample of it. @erluxman – Hardy Aug 10 '19 at 15:22
  • after inflating the layout store it in a variable before returning.. it's that easy – erluxman Aug 11 '19 at 12:36
  • 11
    Is there a DataBinding solution to this? I tried this approach but doesn't seem to work. – YellowJ Oct 07 '19 at 13:06
  • 1
    https://github.com/square/leakcanary/issues/1656 states otherwise. – rupinderjeet Feb 11 '20 at 12:19
  • Is there a way to achieve shared element transition return transition without store the view in a variable? the return transition doesn't work because the view is recreated and storing the view in a variable is causing OOM exceptions – G_comp Mar 26 '20 at 21:28
  • 7
    This answer is a misrepresentation of the cited tweet. Ian explicitly mentions that this is a "constant waste of memory and resources", meaning you should avoid doing this if possible. – Wyko May 12 '20 at 07:34
  • 2
    If you are here and want to avoid at least API call(which is in `onViewCreated` or `onActivityCreated`) that gets called due to re-creation of fragment then move such calls to respective ViewModels and call it inside **init** block. – 333 Oct 02 '20 at 15:33
  • @YellowJ check my answer it my help https://stackoverflow.com/a/67924398/8660721 – Mohammad Sommakia Jun 10 '21 at 15:44
  • This gonna leading to view error when set viewpager2 auto refresh – Thân Hoàng Jun 09 '22 at 09:56
36

You can have a persistent view for your fragment through below implementation

BaseFragment

open class BaseFragment : Fragment(){

        var hasInitializedRootView = false
        private var rootView: View? = null

        fun getPersistentView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?, layout: Int): View? {
            if (rootView == null) {
                // Inflate the layout for this fragment
                rootView = inflater?.inflate(layout,container,false)
            } else {
                // Do not inflate the layout again.
                // The returned View of onCreateView will be added into the fragment.
                // However it is not allowed to be added twice even if the parent is same.
                // So we must remove rootView from the existing parent view group
                // (it will be added back).
                (rootView?.getParent() as? ViewGroup)?.removeView(rootView)
            }

            return rootView
        }
    }

MainFragment

class MainFragment : BaseFragment() {


    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return getPersistentView(inflater, container, savedInstanceState, R.layout.content_main)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (!hasInitializedRootView) {
            hasInitializedRootView = true
            setListeners()
            loadViews()
        }
    }
}

Source

Shahab Rauf
  • 3,651
  • 29
  • 38
  • This saved me big time. Many thanks for posting this – dev2505 Mar 13 '20 at 13:44
  • 3
    The best answer I found on the topic! Thank you champ – orelzion May 20 '20 at 08:51
  • 3
    Livedata is not working (not observing) with this solution. – Kuvonchbek Yakubov Aug 05 '20 at 06:27
  • 1
    Far the best and most simple, but livedata does not work. I can't stop thinking that there are no PROPER approach for this issue. According to the issue tracker on the navigation component github, they are still saying that redrawing was their intent. dam. – March3April4 Sep 15 '20 at 09:53
  • 1
    I'm getting this error: The specified child already has a parent. You must call removeView() on the child's parent first. Also, sometimes the app freezes – G_comp Sep 22 '20 at 01:45
  • The best answer to this topic, I fixed the live data observation issue in my case by re-observing my base LiveData in `getPresistentView()` in the `else` part as follows `(rootView?.parent as? ViewGroup)?.removeView(rootView) viewModel.stateBase.observe(viewLifecycleOwner, Observer { baseRender() }) }` where `baseRender()` is an abstract function that I override in any child fragment – Khaled Ahmed Nov 01 '20 at 10:44
  • can this be modified to be used with data binding ? – vivek panchal May 07 '21 at 03:03
  • 1
    It works but it has side-effect! assume that you are in frag1, then go to frag2 using navigation component, and you come back to frag1 and observer for a result from frag2. the observer wont observe anything and also listeners don't work at all, because hasInitializedRootView is true. – Alireza Noorali May 15 '21 at 06:59
  • @AlirezaNoorali did you find a solution for this problem with the navigation component with bottomNavView when switching to another tab and return for example? – Mohammad Sayed Mar 20 '22 at 11:45
  • @MohammadSayed: No, unfortunately I didn't – Alireza Noorali Mar 26 '22 at 04:51
  • This is a memory leak, please dont use – urSus Apr 11 '22 at 01:41
10

I tried like this, and it works for me.

  • Init ViewModel by navGraphViewModels (Live on Navigation scope)
  • Store any to-restore state in ViewModel
// fragment.kt
private val vm by navGraphViewModels<VM>(R.id.nav_graph) { vmFactory }

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // Restore state
    vm.state?.let {
        (recycler.layoutManager as GridLayoutManager).onRestoreInstanceState(it)
    }
}

override fun onPause() {
    super.onPause()
    // Store state
    vm.state = (recycler.layoutManager as GridLayoutManager).onSaveInstanceState()
}

// vm.kt
var state:Parcelable? = null
Samnang CHEA
  • 643
  • 7
  • 10
4

This will help to speed up fragment creation and when you are using data binding and viewModel, data will still be saved in the view in case back pressed.

just do this:

    lateinit var binding: FragmentConnectBinding
 override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    if (this::binding.isInitialized) {
        binding
    } else {
        binding = FragmentConnectBinding.inflate(inflater, container, false)
        binding.viewModel = viewModel
        binding.model = connectModel
        binding.lifecycleOwner = viewLifecycleOwner
        viewModel.buildAllProfiles()
// do what ever you need to do in first creation
    }
        setupObservers()
        return binding.root
}
Mohammad Sommakia
  • 1,773
  • 3
  • 15
  • 48
3

if you are following to advanced sample from google, they use extension. Here is the modified version of it. In my case I had to show and hide fragment while they were attaching and detaching:

/**
 * Manages the various graphs needed for a [BottomNavigationView].
 *
 * This sample is a workaround until the Navigation Component supports multiple back stacks.
 */
fun BottomNavigationView.setupWithNavController(
    navGraphIds: List<Int>,
    fragmentManager: FragmentManager,
    containerId: Int,
    intent: Intent
): LiveData<NavController> {

    // Map of tags
    val graphIdToTagMap = SparseArray<String>()
    // Result. Mutable live data with the selected controlled
    val selectedNavController = MutableLiveData<NavController>()

    var firstFragmentGraphId = 0

    // First create a NavHostFragment for each NavGraph ID
    navGraphIds.forEachIndexed { index, navGraphId ->
        val fragmentTag = getFragmentTag(index)

        // Find or create the Navigation host fragment
        val navHostFragment = obtainNavHostFragment(
            fragmentManager,
            fragmentTag,
            navGraphId,
            containerId
        )

        // Obtain its id
        val graphId = navHostFragment.navController.graph.id

        if (index == 0) {
            firstFragmentGraphId = graphId
        }

        // Save to the map
        graphIdToTagMap[graphId] = fragmentTag

        // Attach or detach nav host fragment depending on whether it's the selected item.
        if (this.selectedItemId == graphId) {
            // Update livedata with the selected graph
            selectedNavController.value = navHostFragment.navController
            attachNavHostFragment(fragmentManager, navHostFragment, index == 0, fragmentTag)
        } else {
            detachNavHostFragment(fragmentManager, navHostFragment)
        }
    }

    // Now connect selecting an item with swapping Fragments
    var selectedItemTag = graphIdToTagMap[this.selectedItemId]
    val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId]
    var isOnFirstFragment = selectedItemTag == firstFragmentTag

    // When a navigation item is selected
    setOnNavigationItemSelectedListener { item ->
        // Don't do anything if the state is state has already been saved.
        if (fragmentManager.isStateSaved) {
            false
        } else {
            val newlySelectedItemTag = graphIdToTagMap[item.itemId]
            if (selectedItemTag != newlySelectedItemTag) {
                // Pop everything above the first fragment (the "fixed start destination")
                fragmentManager.popBackStack(
                    firstFragmentTag,
                    FragmentManager.POP_BACK_STACK_INCLUSIVE
                )
                val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag)
                        as NavHostFragment

                // Exclude the first fragment tag because it's always in the back stack.
                if (firstFragmentTag != newlySelectedItemTag) {
                    // Commit a transaction that cleans the back stack and adds the first fragment
                    // to it, creating the fixed started destination.
                    if (!selectedFragment.isAdded) {
                        fragmentManager.beginTransaction()
                            .setCustomAnimations(
                                R.anim.nav_default_enter_anim,
                                R.anim.nav_default_exit_anim,
                                R.anim.nav_default_pop_enter_anim,
                                R.anim.nav_default_pop_exit_anim
                            )
                            .add(selectedFragment, newlySelectedItemTag)
                            .setPrimaryNavigationFragment(selectedFragment)
                            .apply {
                                // Detach all other Fragments
                                graphIdToTagMap.forEach { _, fragmentTagIter ->
                                    if (fragmentTagIter != newlySelectedItemTag) {
                                        hide(fragmentManager.findFragmentByTag(firstFragmentTag)!!)
                                    }
                                }
                            }
                            .addToBackStack(firstFragmentTag)
                            .setReorderingAllowed(true)
                            .commit()
                    } else {
                        fragmentManager.beginTransaction()
                            .setCustomAnimations(
                                R.anim.nav_default_enter_anim,
                                R.anim.nav_default_exit_anim,
                                R.anim.nav_default_pop_enter_anim,
                                R.anim.nav_default_pop_exit_anim
                            )
                            .show(selectedFragment)
                            .setPrimaryNavigationFragment(selectedFragment)
                            .apply {
                                // Detach all other Fragments
                                graphIdToTagMap.forEach { _, fragmentTagIter ->
                                    if (fragmentTagIter != newlySelectedItemTag) {
                                        hide(fragmentManager.findFragmentByTag(firstFragmentTag)!!)
                                    }
                                }
                            }
                            .addToBackStack(firstFragmentTag)
                            .setReorderingAllowed(true)
                            .commit()
                    }
                }
                selectedItemTag = newlySelectedItemTag
                isOnFirstFragment = selectedItemTag == firstFragmentTag
                selectedNavController.value = selectedFragment.navController
                true
            } else {
                false
            }
        }
    }

    // Optional: on item reselected, pop back stack to the destination of the graph
    setupItemReselected(graphIdToTagMap, fragmentManager)

    // Handle deep link
    setupDeepLinks(navGraphIds, fragmentManager, containerId, intent)

    // Finally, ensure that we update our BottomNavigationView when the back stack changes
    fragmentManager.addOnBackStackChangedListener {
        if (!isOnFirstFragment && !fragmentManager.isOnBackStack(firstFragmentTag)) {
            this.selectedItemId = firstFragmentGraphId
        }

        // Reset the graph if the currentDestination is not valid (happens when the back
        // stack is popped after using the back button).
        selectedNavController.value?.let { controller ->
            if (controller.currentDestination == null) {
                controller.navigate(controller.graph.id)
            }
        }
    }
    return selectedNavController
}

private fun BottomNavigationView.setupItemReselected(
    graphIdToTagMap: SparseArray<String>,
    fragmentManager: FragmentManager
) {
    setOnNavigationItemReselectedListener { item ->
        val newlySelectedItemTag = graphIdToTagMap[item.itemId]
        val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag)
                as NavHostFragment
        val navController = selectedFragment.navController
        // Pop the back stack to the start destination of the current navController graph
        navController.popBackStack(
            navController.graph.startDestination, false
        )
    }
}

private fun BottomNavigationView.setupDeepLinks(
    navGraphIds: List<Int>,
    fragmentManager: FragmentManager,
    containerId: Int,
    intent: Intent
) {
    navGraphIds.forEachIndexed { index, navGraphId ->
        val fragmentTag = getFragmentTag(index)


        // Find or create the Navigation host fragment
        val navHostFragment = obtainNavHostFragment(
            fragmentManager,
            fragmentTag,
            navGraphId,
            containerId
        )
        // Handle Intent
        if (navHostFragment.navController.handleDeepLink(intent)
            && selectedItemId != navHostFragment.navController.graph.id
        ) {
            this.selectedItemId = navHostFragment.navController.graph.id
        }
    }
}

private fun detachNavHostFragment(
    fragmentManager: FragmentManager,
    navHostFragment: NavHostFragment
) {
    fragmentManager.beginTransaction()
        .hide(navHostFragment)
        .commitNow()
}

private fun attachNavHostFragment(
    fragmentManager: FragmentManager,
    navHostFragment: NavHostFragment,
    isPrimaryNavFragment: Boolean,
    fragmentTag: String
) {
    if (navHostFragment.isAdded) return
    fragmentManager.beginTransaction()
        .add(navHostFragment, fragmentTag)
        .apply {
            if (isPrimaryNavFragment) {
                setPrimaryNavigationFragment(navHostFragment)
            }
        }
        .commitNow()

}

private fun obtainNavHostFragment(
    fragmentManager: FragmentManager,
    fragmentTag: String,
    navGraphId: Int,
    containerId: Int
): NavHostFragment {
    // If the Nav Host fragment exists, return it
    val existingFragment = fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment?
    existingFragment?.let { return it }

    // Otherwise, create it and return it.
    val navHostFragment = NavHostFragment.create(navGraphId)
    fragmentManager.beginTransaction()
        .add(containerId, navHostFragment, fragmentTag)
        .commitNow()
    return navHostFragment
}

private fun FragmentManager.isOnBackStack(backStackName: String): Boolean {
    val backStackCount = backStackEntryCount
    for (index in 0 until backStackCount) {
        if (getBackStackEntryAt(index).name == backStackName) {
            return true
        }
    }
    return false
}

private fun getFragmentTag(index: Int) = "bottomNavigation#$index"
Nurseyit Tursunkulov
  • 8,012
  • 12
  • 44
  • 78
2

Though I think NavigationAdvancedSample is a better solution, I also resolved this issue using @shahab-rauf's code. Because I don't have enough time to apply that into my project.

Base Fragment

abstract class AppFragment: Fragment() {

    private var persistingView: View? = null

    private fun persistingView(view: View): View {
        val root = persistingView
        if (root == null) {
            persistingView = view
            return view
        } else {
            (root.parent as? ViewGroup)?.removeView(root)
            return root
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
        val p = if (persistingView == null) {
            onCreatePersistentView(inflater, container, savedInstanceState)
        } else {
            persistingView // prevent inflating
        }
        if (p != null) {
            return persistingView(p)
        }
        return super.onCreateView(inflater, container, savedInstanceState)
    }

    protected open fun onCreatePersistentView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
        return null
    }

    override fun onViewCreated(view: View, savedInstanceState:Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        if (persistingView != null) {
            onPersistentViewCreated(view, savedInstanceState)
        }
    }

    protected open fun onPersistentViewCreated(view: View, savedInstanceState: Bundle?) {
        logv("onPersistentViewCreated")
    }
}

Implements

class DetailFragment : AppFragment() {
    override fun onCreatePersistentView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // I used data-binding
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_program_detail, container, false)
        binding.model = viewModel
        binding.lifecycleOwner = this
        return binding.root
    }

    override fun onPersistentViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onPersistentViewCreated(view, savedInstanceState)
        
        // RecyclerView bind with adapter
        binding.curriculumRecycler.adapter = adapter
        binding.curriculumRecycler.apply {
            layoutManager = LinearLayoutManager(context)
            setHasFixedSize(true)
        }
        viewModel.curriculums.observe(viewLifecycleOwner, Observer {
            adapter.applyItems(it ?: emptyList())
        })

        viewModel.refresh()
    }
}
Brownsoo Han
  • 4,549
  • 3
  • 20
  • 20
  • 1
    I re-read the question 2 times and didn't find any mention about binding. More over binding even doesn't declare as variable. It's just appear from nowhere... And how this binding is helping to prevent destroying of fragment? – Eugene Troyanskii Feb 10 '22 at 14:21
1

This is same answer as suggested by @Shahab Rauf only extra thing is inclusion of Databinding and implementing onCreateView only in BaseFragment instead of Child Fragments. And also initializing navController in onViewCreated() of BaseFragment.

BaseFragment

abstract class BaseFragment<T : ViewDataBinding, VM : BaseViewModel<UiState>> : Fragment() {

protected lateinit var binding: T
var hasInitializedRootView = false
private var rootView: View? = null

protected abstract val mViewModel: ViewModel
protected lateinit var navController: NavController

fun getPersistentView(
    inflater: LayoutInflater?,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
    layout: Int
): View? {
    if (rootView == null) {
        binding = DataBindingUtil.inflate(inflater!!, getFragmentView(), container, false)
        //setting the viewmodel
        binding.setVariable(BR.mViewModel, mViewModel)
        // Inflate the layout for this fragment
        rootView = binding.root
    } else {
        // Do not inflate the layout again.
        // The returned View of onCreateView will be added into the fragment.
        // However it is not allowed to be added twice even if the parent is same.
        // So we must remove rootView from the existing parent view group
        // (it will be added back).
        (rootView?.getParent() as? ViewGroup)?.removeView(rootView)
    }

    return rootView
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? = getPersistentView(inflater, container, savedInstanceState, getFragmentView())


//this method is used to get the fragment layout file
abstract fun getFragmentView(): Int

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    navController = Navigation.findNavController(view)
}
}

HomeFragment (Any Fragment that is extending BaseFragment)

class HomeFragment : BaseFragment<HomeFragmentBinding, HomeViewModel>(),
RecycleViewClickListener {

override val mViewModel by viewModel<HomeViewModel>()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    if (!hasInitializedRootView) {hasInitializedRootView = true
        setListeners()
        loadViews()
         --------

}
Murli
  • 524
  • 6
  • 11
1

Hi the problem is fixed in latest version 2.4.0-alpha01 now there is a official support for multiple backstack navigation

Checkout the link: https://developer.android.com/jetpack/androidx/releases/navigation#version_240_2

SURYA N
  • 299
  • 2
  • 16
  • 13
    This is a great feature, but I think it doesn't help in any way with the problem related: avoid recreating the view. – pauminku May 25 '21 at 05:37
0

For Java Developers, As described and combined from above answers,

BaseFragment.java

public abstract class BaseFragment<T extends ViewDataBinding, V extends BaseViewModel> extends Fragment {

    private View mRootView;
    private T mViewDataBinding;
    private V mViewModel;
    public boolean hasInitializedRootView = false;
    private View rootView = null;

    public View getPersistentView(LayoutInflater layoutInflater, ViewGroup container, Bundle saveInstanceState, int layout) {

        if (rootView == null) {
            mViewDataBinding = DataBindingUtil.inflate(layoutInflater, layout, container, false);
            mViewDataBinding.setVariable(getBindingVariable(),mViewModel);
            rootView = mViewDataBinding.getRoot();
        }else {
            // Do not inflate the layout again.
            // The returned View of onCreateView will be added into the fragment.
            // However it is not allowed to be added twice even if the parent is same.
            // So we must remove rootView from the existing parent view group
            // (it will be added back).
            ViewGroup viewGroup = (ViewGroup) rootView.getParent();
            if (viewGroup != null){
                viewGroup.removeView(rootView);
            }
        }
        return rootView;
    }
}

Implement in Your Fragment as,

@AndroidEntryPoint
public class YourFragment extends BaseFragment<YourFragmentBinding, YourViewModel> {


@Override
    public View onCreateView(@NonNull @NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return getPersistentView(inflater, container, savedInstanceState, getLayoutId());
    }


@Override
    public void onViewCreated(@NonNull @NotNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (!hasInitializedRootView){
            hasInitializedRootView = true;
            // do your work here

        }

    }


}
iamnaran
  • 1,894
  • 2
  • 15
  • 24
0

If you just want to check whether your fragment got recreated ,we can simply override the onCreate() that called only once in lifetime for fragment.

    override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //Your onetime operation or function call here.
        }
Soft Code
  • 49
  • 5