使用导航图范围注入视图模型: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()
你知道我该怎么做吗?
简而言之,你可以用匕首Provider
或Lazy
懒惰地提供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 重新创建附加到 FragmentManger
的 Activity
和 Fragments
与 NavController
之间的某种竞争条件可以提供。更具体地说:
- 附有
Fragments
的 Activity
被 OS 摧毁(可以用 "don't keep Activities" 从 "developer settings" 复制)
- 用户导航回
Activity
,OS 继续重新创建 Activity
Activity
在重建时调用 setContentView
。
- 这会导致
FragmentManager
中的 Fragments
重新附加,这涉及调用 Fragment#onAttach
Fragment
被注入Fragment#onAttach
- 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)}
这里有一些解决方法:
我在我的应用程序中使用了一个导航组件,并且还在同一图表中的多个片段之间使用了共享的 ViewModel。现在我想用 this.
这个图形范围实例化 ViewModel如您所知,片段 we should inject objects ( ViewModel,..etc ) in onAttach
:
但是当我想这样做时(在 onAttach
中注入具有图形范围的 ViewModel),出现此错误:
IllegalStateException: NavController is not available before onCreate()
你知道我该怎么做吗?
简而言之,你可以用匕首Provider
或Lazy
懒惰地提供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 重新创建附加到 FragmentManger
的 Activity
和 Fragments
与 NavController
之间的某种竞争条件可以提供。更具体地说:
- 附有
Fragments
的Activity
被 OS 摧毁(可以用 "don't keep Activities" 从 "developer settings" 复制) - 用户导航回
Activity
,OS 继续重新创建Activity
Activity
在重建时调用setContentView
。- 这会导致
FragmentManager
中的Fragments
重新附加,这涉及调用Fragment#onAttach
Fragment
被注入Fragment#onAttach
- 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)}
这里有一些解决方法: