Search code examples
androidandroid-fragmentsandroid-lifecycleandroid-splashscreen

Fragment getting initialised before Android 12 Splash screen ends


I have a FragmentContainerView in my MainActivity which uses a nav graph.

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nav_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="true"
    app:navGraph="@navigation/nav" />

I want my fragments to wait till I get some data from an API before I hide my splash screen. I am using android 12 Splash Screen. This is how I am trying to accomplishing it(from documentation):

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    installSplashScreen()
    setContentView(R.layout.activity_main)
    //Set up an OnPreDrawListener to the root view.
    val content: View = findViewById(android.R.id.content)
    content.viewTreeObserver.addOnPreDrawListener(
        object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                // Check if the initial data is ready.
                return if (mainActivityViewModel.isDataReady()) {
                    // The content is ready; start drawing.
                    Timber.tag("Splash").d("data ready")
                    content.viewTreeObserver.removeOnPreDrawListener(this)
                    true
                    } else {
                        Timber.tag("Splash").d("data not ready")
    
                        // The content is not ready; suspend.
                        false
                        }
                    }
                }
        )

The Splash screen is going away only after I get the data. But the issue is that my Fragments callbacks like onViewCreated gets invoked even before my data is ready. This is causing issues because I rely on data that I fetch during splash screen to do some tasks.

How can I make sure, my fragments won't get initialised before my Splash screen goes away??


Solution

  • The Splash screen is going away only after I get the data. But the issue is that my Fragments callbacks like onViewCreated gets invoked even before my data is ready.

    Activity and fragment lifecycle callbacks are only triggered by the system on the appropriate times. They can't be triggered by developers; there is no control on that. So, you can't stop onCreateView, onViewCreated,.. from taking place. i.e. you can't stop activity/fragment from initialization; otherwise you could have ANRs.

    More in detail

    Initializations of activities/fragments shouldn't be seized until some background task finishes; this literally will cause ANRs.

    While you're seizing the drawing of the activity's root layout using addOnPreDrawListener; this doesn't mean that the activity's initialization (i.e. onCreate()) get seized, because both are working asynchronously, and the onCreate(), onStart(), & onResume() will get return normally.

    The same is true for fragment's callbacks where the fragment's initialization (i.e. onCreateView(), onViewCreated...) is coupled with the activity's onCreate().

    Now, as the initialization of the start destination fragment relies on the API data; then this particular fragment shouldn't be the start destination.

    So, to fix this you need to create a some splash screen fragment as the start destination that doesn't rely on the API data.

    Depending on the API incoming data, you can decide a navigation to the original fragment through the navController:

    return if (mainActivityViewModel.isDataReady()) {
        
        // Do the transaction to the original fragment through the navController
        
        // The content is ready; start drawing.
        Timber.tag("Splash").d("data ready")
        content.viewTreeObserver.removeOnPreDrawListener(this)
        true
        
        } else {
        
            // Not transaction is needed, keep the splash screen fragment
        
            Timber.tag("Splash").d("data not ready")
    
            // The content is not ready; suspend.
            false
            }
        }
    }