Viewed
Viewed

Reputation: 1333

Why HIlt can not create instance of SharedViewModel?

Update 1

I changed delegate from viewModels to hiltNavGraphViewModels and it's works. Thanks to ianhanniballake's comments. But now is another issue. If app have killed (Logcat -> Terminate App), occurs exception No destination with ID 2131296453 is on the NavController's back stack. The current destination is null (stacktrace below). How can I restore back stack in order to hiltNavGraphViewModels don't fail?

@AndroidEntryPoint
class ScriptDetailFragment: Fragment(R.layout.component_detail_script) {

    private val viewModel: SharedViewModel by hiltNavGraphViewModels(R.id.scriptFragment)
}

@AndroidEntryPoint
class ScriptFragment : Fragment(R.layout.component_script) {

    private val viewModel: SharedViewModel by hiltNavGraphViewModels(R.id.scriptFragment)

    fun navigateToDetail(navDirection: NavDirections) {
        findNavController().navigate(navDirection)
    }
}

@HiltViewModel
class SharedViewModel @Inject constructor(
    private val componentWrapper: ComponentWrapper
) : ViewModel() {

}
dependencies {
    implementation 'androidx.core:core-ktx:1.8.0'

    implementation 'com.google.dagger:hilt-android:2.38'
    implementation("androidx.hilt:hilt-navigation-fragment:1.0.0")
    kapt 'com.google.dagger:hilt-compiler:2.38'
    kapt 'androidx.hilt:hilt-compiler:1.0.0'

    implementation "androidx.activity:activity-ktx:1.5.1"
    implementation "androidx.fragment:fragment-ktx:1.5.4"
    implementation "androidx.navigation:navigation-fragment-ktx:2.5.3"
    implementation "androidx.navigation:navigation-ui-ktx:2.5.3"
}
 Caused by: java.lang.IllegalArgumentException: No destination with ID 2131296453 is on the NavController's back stack. The current destination is null
        at androidx.navigation.NavController.getBackStackEntry(NavController.kt:2209)
        at *.pages.departure.ScriptFragment$special$$inlined$hiltNavGraphViewModels$1.invoke(HiltNavGraphViewModelLazy.kt:49)
        at *.pages.departure.ScriptFragment$special$$inlined$hiltNavGraphViewModels$1.invoke(Unknown Source:0)
        at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
        at *.pages.departure.ScriptFragment$special$$inlined$hiltNavGraphViewModels$3.invoke(HiltNavGraphViewModelLazy.kt:57)
        at *.pages.departure.ScriptFragment$special$$inlined$hiltNavGraphViewModels$3.invoke(Unknown Source:0)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:47)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelLazy.kt:35)
        at *.pages.departure.ScriptFragment.getViewModel(ScriptFragment.kt:15)
        at *.pages.departure.ScriptFragment.onViewCreated(ScriptFragment.kt:20)
        at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3128)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
        at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1433)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2977)
        at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:2888)
        at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3129)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
        at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1433)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2977)
        at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2895)
        at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263)
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:351)
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:248)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1335)
        at android.app.Activity.performStart(Activity.java:7043)

activity_main.xml

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        tools:context=".screens.MainActivity" />

MainActivity.ki

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        _binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        configureNavHost()
    }

    override fun onSupportNavigateUp() = navController.navigateUp() || super.onSupportNavigateUp()

    private fun configureNavHost() {
        val resId = if (dsr.isFirstLaunch.value) R.id.welcomeFragment else R.id.webViewFragment
        navController = (supportFragmentManager.findFragmentById(R.id.nav_host) as NavHostFragment).navController
        navController.navInflater.inflate(R.navigation.nav_graph).let { graph ->
            graph.setStartDestination(resId)
            navController.graph = graph
        }
    }
}

Upvotes: 0

Views: 607

Answers (1)

diziaq
diziaq

Reputation: 7815

If the app is killed and then restarted, the Navigation graph and the back stack will be reset. This means that any fragments or destinations that were on the back stack will be removed, and the NavController will no longer be able to find them.

In order to restore the back stack and avoid the exception, you can use the onSaveInstanceState() method in Fragment or Activity to save the state of the NavController and the back stack. This will allow to restore the stored state when the app is restarted.

Example of implementation in a Fragment:

@AndroidEntryPoint
class MyFragment : Fragment() {

    private val navController by lazy { findNavController() }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // Save the state of the NavController and the back stack
        navController.saveState(outState)
    }

    override fun onViewStateRestored(savedInstanceState: Bundle?) {
        super.onViewStateRestored(savedInstanceState)
        // Restore the state of the NavController and the back stack
        savedInstanceState?.let { navController.restoreState(it) }
    }
}

With this implementation, the state of the NavController and the back stack will be saved when the app is killed and restored when the app is restarted, allowing the hiltNavGraphViewModels() function to find the correct destination and avoid the exception.

Upvotes: 2

Related Questions