16

Currently, I am playing around Android Navigation Component with Bottom Navigation Bar. While playing I realized two facts:

  • Fragments are always recreated (onCreate, onViewCreated, onViewDestroyed are called as soon as the user navigates to another fragment)
  • savedInstanceState is always null (in onCreate, onViewCreated, etc.)

The first issue can be fixed by using custom FragmentNavigator, which will reuse fragment if it already exists

package am.chamich.apps.advancedbottomnavigation.navigator

import android.content.Context
import android.os.Bundle
import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import androidx.navigation.fragment.FragmentNavigator


@Navigator.Name("retain_state_fragment")
class RetainStateFragmentNavigator(
    private val context: Context,
    private val manager: androidx.fragment.app.FragmentManager,
    private val containerId: Int
) : FragmentNavigator(context, manager, containerId) {

    override fun navigate(
        destination: Destination,
        args: Bundle?,
        navOptions: NavOptions?,
        navigatorExtras: Navigator.Extras?
    ): NavDestination? {
        val tag = destination.id.toString()
        val transaction = manager.beginTransaction()

        val currentFragment = manager.primaryNavigationFragment
        if (currentFragment != null) {
            transaction.detach(currentFragment)
        }

        var fragment = manager.findFragmentByTag(tag)
        if (fragment == null) {
            val className = destination.className
            fragment = instantiateFragment(context, manager, className, args)
            transaction.add(containerId, fragment, tag)
        } else {
            transaction.attach(fragment)
        }

        transaction.setPrimaryNavigationFragment(fragment)
        transaction.setReorderingAllowed(true)
        transaction.commit()

        return destination
    }
}

Question

For the second issue, I have no idea how to fix it, actually, I even didn't understand how the fragment is restoring its state (for example when you rotate the screen), I tied to use fragment.setInitialSavedState(savedState) to save and restore fragment state, but that doesn't help in this situation.

Actually what I need to know is when fragment view was recreated

Here is a link to my GitHub project, any help is welcome.

Community
  • 1
  • 1
  • you can check the lifecyle of fragments here https://developer.android.com/guide/components/fragments – Ergin Ersoy Apr 22 '19 at 21:02
  • 1
    @ErginErsoy I know the lifecycle of fragments, but that does not help to fix the issue, the issue is that saveInstanceState is always null, even when the fragment is reattached –  Apr 22 '19 at 21:03
  • did you check this answer for that issue https://stackoverflow.com/questions/20550016/savedinstancestate-is-always-null-in-fragment – Ergin Ersoy Apr 22 '19 at 21:06
  • @ErginErsoy yes I checked it, I even tried it. Doesn't work at all :( –  Apr 22 '19 at 21:07
  • @ErginErsoy you can check out my project and try to run it, there are quite a huge amount of logs when clicking on navigation items you will see in the logs that `saveinstance` state is always null :( –  Apr 22 '19 at 21:18
  • Firstly, your RetainStateFragmentNavigator is not being used at all. To use it, do the following: 1. Extend MyNavigationHostFragment from NagivationHostFragment 2. Override createFragmentNavigator() function in MyNavigationHostFragment override fun createFragmentNavigator(): Navigator { return RetainStateFragmentNavigator(requireContext(), childFragmentManager, id) } 3. Use MyNavigationHostFragment as in place of default navigation host fragment in your activity_main.xml 4. Now debug further to resolve the issue. – Rahul Shukla Apr 30 '19 at 13:34

2 Answers2

0

Fragment will save its state only when activity is recreated (e.g. screen rotation) and changing the fragment doesn't matter. From documentation:

There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state.

Source

Saving custom state:

Put this method inside fragment:

override fun onSaveInstanceState(outState: Bundle) {
    outState.putString("text", "some value")
    super.onSaveInstanceState(outState)
}

And read the value, for example, inside onViewCreated:

val text = savedInstanceState?.getString("text")

You will receive desired value after screen rotation / phone language change or other config changes - when activity (and fragment) is being recreated.

Derek K
  • 2,756
  • 1
  • 21
  • 37
-1

Check this Blog, http://www.androiddevelopment.co.in/2019/05/how-to-save-android-activity-state.html, This Blog explain how to save activity state when the activity is destroyed.

For example, If you change the language of your phone while the activity was running (and so different resources from your project need to be loaded). Another very common scenario is when you rotate your phone to the side so that the activity is recreated and displayed in landscape. You can use this technique to store instance values for your application (selections, unsaved text, etc.).

SHIDHIN TS
  • 1,557
  • 3
  • 26
  • 58