将视图模型注入导航图范围:在onCreate()

时间:2019-12-18 08:36:46

标签: android dagger-2 android-viewmodel android-architecture-navigation fragment-lifecycle

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

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

但是当我要执行此操作(在onAttach中向ViewModel注入图形作用域)时,会发生此错误:

IllegalStateException: NavController is not available before onCreate()

你该怎么办?

2 个答案:

答案 0 :(得分:7)

简而言之,您可以为ViewModel加上匕首ProviderLazy

详细的解释是:

您的注射点正确。根据{{​​1}}

DaggerActivity立即在以下位置调用AndroidInjection.inject() onCreate(),在调用super.onCreate()之前,DaggerFragment会执行 在onAttach()中也是如此。

问题是在Android重新创建https://dagger.dev/android#when-to-injectActivity所附加的FragmentsFragmentManger到提供NavController之间存在某种竞争状况。更具体地说:

  1. 一个附加了Activity的{​​{1}}被操作系统破坏了(可以从“开发人员设置”中复制为“不要保留活动”)
  2. 用户导航回到Fragments,操作系统继续重新创建Activity
  3. Activity在重新创建时调用Activity
  4. 这将导致setContentView中的Fragments被重新连接,这涉及调用FragmentManager
  5. Fragment#onAttach注入Fragment
  6. Dagger尝试提供Fragment#onAttach

但是您目前无法从NavController获得NavController,因为Activity尚未完成,您可以得到

Activity#onCreate

我发现的解决方案是注入IllegalStateException: NavController is not available before onCreate() 或依赖NavCotroller的东西(例如NavController,因为Android需要ViewModel才能获得导航。范围内的NavController)。这可以通过两种方式完成:

  • VideModels
  • Lazy

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

ie:将Provided注入到ViewModel或这样的导航器实现中:

Fragment

然后像这样使用它:

    @Inject
    lateinit var viewModel: Provider<ViewModel>

现在,viewModel.get().events.observe(this) {....} 可以由Dagger提供,例如:

ViewModel

在注入 @Provides fun provideViewModel( fragment: Fragment, argumentId: Int ): CreateMyViewModel { val viewModel: CreateMyViewModel by fragment.navGraphViewModels(R.id.nested_graph_id) return viewModel } 时,Dagger不会尝试解决配置,但是在使用它时,竞争条件将得到解决。

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

答案 1 :(得分:0)

...我们应该在onAttach中注入对象(ViewModel,.. etc)...

使用by navGraphViewModels(R.id.nav_graph)包提供的原始androidx.navigation委托属性,目前看来是不可行的,因为是从源代码开始的

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