首页 > 解决方案 > 使用导航图范围注入视图模型:在 onCreate() 之前 NavController 不可用

问题描述

我在我的应用程序中使用导航组件,并且还在同一个图中的多个片段之间使用共享 ViewModel。现在我想用 this 实例化带有这个图形范围的ViewModel

如您所知,我们应该在片段中注入对象(ViewModel,..etc)onAttach

但是当我想这样做(在 中注入带有图形范围的 ViewModel onAttach)时,会发生此错误:

IllegalStateException: NavController is not available before onCreate()

你知道我该怎么做吗?

标签: androiddagger-2android-viewmodelandroid-architecture-navigationfragment-lifecycle

解决方案


简而言之,您可以为ViewModel懒惰提供 daggerProviderLazy.

长的解释是:

您的注射点是正确的。根据https://dagger.dev/android#when-to-inject

DaggerActivity 在调用 super.onCreate() 之前立即在 onCreate() 中调用 AndroidInjection.inject(),而 DaggerFragment 在 onAttach() 中执行相同的操作。

Activity问题是在 Android 重新创建和Fragments附加到的FragmentManger时间和NavController可以提供的时间之间的某种竞争条件。进一步来说:

  1. Activity已附加的一个已Fragments被操作系统破坏(可以通过“开发人员设置”中的“不保留活动”进行复制)
  2. 用户导航回Activity,操作系统继续重新创建Activity
  3. ActivitysetContentView重新创建时调用。
  4. 这会导致FragmentsinFragmentManager重新连接,这涉及调用Fragment#onAttach
  5. Fragment注入Fragment#onAttach
  6. Dagger 试图提供NavController

但是你不能从这一点上得到NavControllerActivity因为Activity#onCreate还没有完成,你得到

IllegalStateException: NavController is not available before onCreate()

我找到的解决方案是懒惰地注入提供NavCotroller或依赖于的东西NavController(例如ViewModel,因为Android需要NavControllernav-scoped VideModels)。这可以通过两种方式完成:

  • Lazy
  • Provided

(参考:https ://proandroiddev.com/dagger-2-part-three-new-possibilities-3daff12f7ebf )

即:注入ViewModelFragment导航器的或实现中,如下所示:

    @Inject
    lateinit var viewModel: Provider<ViewModel>

然后像这样使用它:

viewModel.get().events.observe(this) {....}

现在,ViewModel由 Dagger 提供的可以像:


    @Provides
    fun provideViewModel(
        fragment: Fragment,
        argumentId: Int
    ): CreateMyViewModel {

        val viewModel: CreateMyViewModel
                by fragment.navGraphViewModels(R.id.nested_graph_id)

        return viewModel
    }

Dagger 在注入时不会尝试解决配置问题Fragment,但在使用时,竞争条件将得到解决。

我真的很讨厌不能直接使用我的 viewModels 并且需要使用Provider,但这是我看到的解决这个问题的唯一解决方法,我敢肯定这是谷歌的疏忽(我不怪他们,因为跟踪片段和活动的荒谬生命周期是如此困难)。


推荐阅读