我在我的项目中仅使用dagger2(而不是dagger-android)。使用multibinding注入ViewModel很好。但是以前没有dagger2的地方存在一个问题,我在多个片段中使用了活动中使用的相同viewmodel实例(使用fragment-ktx方法activityViewModels()),但是现在由于dagger2注入了视图模型,因此它总是提供新实例(检查每个片段的viewmodel的每个片段中的hashCode),这只是中断了使用viewmodel的片段之间的通信。
片段和视图模型代码如下:
class MyFragment: Fragment() {
@Inject lateinit var chartViewModel: ChartViewModel
override fun onAttach(context: Context) {
super.onAttach(context)
(activity?.application as MyApp).appComponent.inject(this)
}
}
//-----ChartViewModel class-----
class ChartViewModel @Inject constructor(private val repository: ChartRepository) : BaseViewModel() {
//live data code...
}
这是视图模型依赖注入的代码:
//-----ViewModelKey class-----
@MapKey
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
//-----ViewModelFactory class------
@Singleton
@Suppress("UNCHECKED_CAST")
class ViewModelFactory
@Inject constructor(
private val viewModelMap: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = viewModelMap[modelClass] ?: viewModelMap.asIterable()
.firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
?: throw IllegalArgumentException("Unknown ViewModel class $modelClass")
return try {
creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
//-----ViewModelModule class-----
@Module
abstract class ViewModelModule {
@Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@Binds
@IntoMap
@ViewModelKey(ChartViewModel::class)
abstract fun bindChartViewModel(chartViewModel: ChartViewModel): ViewModel
}
有什么方法可以为多个片段实现相同的视图模型实例,也可以同时将视图模型注入片段中。 另外,也需要bindViewModelFactory方法,因为即使没有此方法,它似乎也对应用程序没有影响。
一种解决方法是为共享通用视图模型的片段制作BaseFragment,但这又包括样板代码,而且我也不是BaseFragment / BaseActivity的忠实拥护者。
这是为ChartViewModel生成的代码,该代码始终创建viewModel的newInstance:
@SuppressWarnings({
"unchecked",
"rawtypes"
})
public final class ChartViewModel_Factory implements Factory<ChartViewModel> {
private final Provider<ChartRepository> repositoryProvider;
public ChartViewModel_Factory(Provider<ChartRepository> repositoryProvider) {
this.repositoryProvider = repositoryProvider;
}
@Override
public ChartViewModel get() {
return newInstance(repositoryProvider.get());
}
public static ChartViewModel_Factory create(Provider<ChartRepository> repositoryProvider) {
return new ChartViewModel_Factory(repositoryProvider);
}
public static ChartViewModel newInstance(ChartRepository repository) {
return new ChartViewModel(repository);
}
}
问题是当您像这样注入视图模型时
匕首只是创建一个新的viewmodel实例。没有viewmodel-fragment-lifecycle魔术,因为此ViewModel不在活动/片段的ViewModel存储中,并且您创建的ViewModelFactory并未提供该ViewModel。在这里,您可以将viewmodel视为任何普通类。举个例子:
Your viewmodel is equivalent to this
AnyClass
because the viewmodel is not in the viewmodelstore and not scoped to the lifecycle of the fragment/activity.否。由于上述原因。
It does not have any effect because (I'm assuming that) you are not using the
ViewModelFactory
anywhere. Since it's not referenced anywhere, this dagger code for the viewmodelfactory is useless.Here's what @binds is doing: 1 2
这就是为什么删除它对应用程序没有影响的原因。
那么解决方案是什么?您需要将工厂注入片段/活动中,并使用工厂获取视图模型的实例
What is
X
here? X isViewModelStoreOwner
. AViewModelStoreOwner
is something that keeps track of the viewmodels under them.ViewModelStoreOwner
is implemented by activity and fragment. So you have a few ways of creating a viewmodel:Yes, this is indeed a bad idea. The solution is to use
requireParentFragment()
andrequireActivity()
to get the viewmodel instance. But you'll be writing the same code multiple times in every fragment/activity that has a viewmodel. For that you can abstract away thisViewModelProvider(x, factory)
part in a base fragment/activity class and also inject the factory in the base classes, which will simplify your child fragment/activity code like this:You can share
ViewModel
between fragments when instantiating if the fragments has the same parent activity片段一
片段二
Add your ViewModel as
PostListViewModel
insideViewModelModule
:To end with, our activity will have
ViewModelProvider.Factory
injected and it will be passed to theViewModelProviders.of()
method as the factory (second parameter)For more check this post:Inject ViewModel with Dagger2 And Check github