【问题标题】:injecting viewmodel with navigation-graph scope: NavController is not available before onCreate()使用导航图范围注入视图模型:NavController 在 onCreate() 之前不可用
【发布时间】:2019-12-18 08:36:46
【问题描述】:

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

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

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

IllegalStateException: NavController is not available before onCreate()

你知道我该怎么做吗?

【问题讨论】:

  • 你在 onStart() 中使用的是 NavController 吗?
  • 不在onStart() 中。我使用 NavController 在 onAttach() @MohammedRampurawala 中获取 navGraph 的 viewModelStore
  • 在调用 onCreate() 后需要使用 NavController。这意味着您的活动视图已初始化。
  • 技术上你也可以在onCreate而不是onAttach里面做。

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


【解决方案1】:

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

长解释是:

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

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

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

  1. 一个附有FragmentsActivity 被操作系统销毁(可以通过“开发者设置”中的“不要保留活动”进行复制)
  2. 用户导航回Activity,操作系统继续重新创建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

(参考: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 不会尝试解析配置,但是当它被使用时,竞争条件将得到解决。

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

【讨论】:

  • 嘿,这里是菜鸟。我怎样才能提供片段?我现在正在使用@ContributesAndroidInjector abstract fun contributeDetailFragment(): DetailsFragment
【解决方案2】:

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

目前看来,使用 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多