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

bei*_*rad 7 android fragment-lifecycle dagger-2 android-viewmodel android-architecture-navigation

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

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

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

IllegalStateException: NavController is not available before onCreate()
Run Code Online (Sandbox Code Playgroud)

你知道我怎么做吗?

GaR*_*eTa 10

简而言之,您可以为ViewModel懒惰的人提供匕首ProviderLazy.

长的解释是:

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

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

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

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

但是此时您无法NavController从获得Activity,因为Activity#onCreate尚未完成并且您得到

IllegalStateException: NavController is not available before onCreate()
Run Code Online (Sandbox Code Playgroud)

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

  • Lazy
  • Provided

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

即:将 注入ViewModelFragmentnavigator的或实现中,如下所示:

    @Inject
    lateinit var viewModel: Provider<ViewModel>
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

viewModel.get().events.observe(this) {....}
Run Code Online (Sandbox Code Playgroud)

现在,ViewModel可以由 Dagger 提供,例如:


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

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

        return viewModel
    }
Run Code Online (Sandbox Code Playgroud)

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

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