使用导航图范围注入视图模型:NavController 在 onCreate() 之前不可用

injecting viewmodel with navigation-graph scope: NavController is not available before onCreate()

我在我的应用程序中使用了一个导航组件,并且还在同一图表中的多个片段之间使用了共享的 ViewModel。现在我想用 this.

这个图形范围实例化 ViewModel

如您所知,片段 we should inject objects ( ViewModel,..etc ) in onAttach

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

IllegalStateException: NavController is not available before onCreate()

你知道我该怎么做吗?

简而言之,你可以用匕首ProviderLazy懒惰地提供ViewModel

长解释是:

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

DaggerActivity calls AndroidInjection.inject() immediately in onCreate(), before calling super.onCreate(), and DaggerFragment does the same in onAttach().

问题是 Android 重新创建附加到 FragmentMangerActivityFragmentsNavController 之间的某种竞争条件可以提供。更具体地说:

  1. 附有 FragmentsActivity 被 OS 摧毁(可以用 "don't keep Activities" 从 "developer settings" 复制)
  2. 用户导航回 Activity,OS 继续重新创建 Activity
  3. Activity 在重建时调用 setContentView
  4. 这会导致 FragmentManager 中的 Fragments 重新附加,这涉及调用 Fragment#onAttach
  5. Fragment被注入Fragment#onAttach
  6. Dagger 尝试提供 NavController

但是此时您无法从 Activity 获取 NavController,因为 Activity#onCreate 尚未完成,您将获取

IllegalStateException: NavController is not available before onCreate()

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

  • Lazy
  • Provided

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

即:将 ViewModel 注入 Fragment 或像这样实现导航器:

    @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
    }

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

我真的很讨厌不能直接使用我的 viewModels 而需要使用 Provider,但这是我看到的解决这个问题的唯一解决方法,我确信这是 Google(我不怪他们,因为跟踪 Fragment 和 Activity 荒谬的生命周期是如此困难)。

...we should inject objects ( ViewModel,..etc ) in onAttach...

看起来目前无法使用 androidx.navigation 包提供的原始 by navGraphViewModels(R.id.nav_graph) 委托 属性 进行此类注入,因为从源代码来看

findNavController().getBackStackEntry(navGraphId)

public final NavController getNavController() 它指出:

 * Returns the {@link NavController navigation controller} for this navigation host.
 * This method will return null until this host fragment's {@link #onCreate(Bundle)}

这里有一些解决方法:

https://github.com/InsertKoinIO/koin/issues/442