Search code examples
androidandroid-fragmentsandroid-navigationandroid-bottomnavigationviewandroid-safe-args

Android: Fragment restore with BottomNavigationView, NavController and SafeArgs


i'm currently working on an Android app and encountered a problem concerning BottomNavigationView and Fragments. I know, there are similar questions like mine but either they doesn't solve my problem or they have no working answers.

My app consists of five top-level destination fragments. For navigating between them I use the BottomNavigationView. Additionally, I have several fragments which serve as lower-level destinations and will be called from one of the top-level fragments. I use SafeArgs plugin to navigate to these fragments and also to pass data to.

My BottomNavigationView Configuration looks like this:

val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = (supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment).navController

val appBarConfiguration = AppBarConfiguration(setOf(
    R.id.navigation_dest1, R.id.navigation_dest2, R.id.navigation_dest3,
    R.id.navigation_dest4, R.id.navigation_dest5))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController);

The problem of this usage is that BottomNavigationView doesn't seem to provide support for saving and storing the fragments somewhere and reuse these instances for navigation. It just creates a new instance and displays it.

Currently each fragment contains some data fetching code, e.g. running a network request in a coroutine or loading files from the filesystem. And because BottomNavigationView doesn't preserve fragment instances, these data fetching parts are run too often.
Of course I thought about putting the data fetching process into the main activity but this results in an overall slower app-startup and doesn't solve the problem that the fragments still need to be recreated every time the user navigates between them.

Up to this point, I already found half of a solution. By using the SupportFragmentManager, manually adding, showing and hiding my fragments it works. But the app runs noticeably slower and the navigation to my lower-level destinations with SafeArgs just doesn't work anymore. I use SafeArgs because it's easy to use and pretty hassle-free, and I would like to keep using it.

I tried to manage it all manually with SupportFragmentManager, but it ends up in chaos and worse performance.

Is there any known way my problem can be solved? A way, BottomNavigationView can interact with SafeArgs and SupportFragmentManager to reuse the fragments instead of recreating them on each navigation action?

(If you need further information or parts of my code, please ask. I think posting my complete code here doesn't make much sense.)


Solution

  • Have you considered the option of sharing a ViewModel with your fragments ? For example:

    Create a ViewModel class like the following:

     class MyViewModel: ViewModel() {
        ....
        ....
        }
    

    Then, because your fragments share the same Activity, you can declare the following (in Kotlin):

     class MyFragment1: Fragment() {
            val viewModel: MyViewModel by activityViewModels()
            ....
            ....  
        }
    
        class MyFragment2: Fragment() {
            val viewModel: MyViewModel by activityViewModels()
            ....
            ....  
    
        }
    

    In this case Fragment1 and Fragment2 will share the same ViewModel instance and the ViewModel will remain in memory until the activity is destroyed. Fragments won't be preserved when you navigate out, but you can preserve all data of each fragment and re-use them. It is fast and smooth and you won't mind if the fragment is re-created because all its data will be kept in memory and ready for use in the shared ViewModel.

    See also the official documentation: ViewModel Overview