gpt4 book ai didi

android - 使用 dagger2 在多个 fragment 中使用相同的 View 模型实例

转载 作者:行者123 更新时间:2023-12-03 21:13:41 25 4
gpt4 key购买 nike

我在我的项目中仅使用 dagger2 ( 而不是 dagger-android )。使用多重绑定(bind)注入(inject) ViewModel 工作正常。但是有一个问题,以前没有 dagger2 我在多个 fragment 的 Activity 中使用相同的 View 模型实例(使用 fragment-ktx 方法 activityViewModels()),但现在因为 dagger2 正在注入(inject)它总是提供新实例的 View 模型 (在每个 fragment 中使用 hashCode 检查)每个 fragment 的 View 模型,这只是破坏了使用 View 模型的 fragment 之间的通信。

fragment 和 View 模型代码如下:

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...
}

这是 View 模型依赖注入(inject)的代码:
//-----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
}

有没有办法 为多个 fragment 实现相同的 View 模型实例,同时在 fragment 中注入(inject) View 模型。
是否还需要 bindViewModelFactory 方法,因为即使没有此方法,它似乎对应用程序也没有影响。

一个 解决方法 可能是做一个 基 fragment 对于共享通用 View 模型的 fragment ,但这将再次包含样板代码,而且我不是 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);
}
}

最佳答案

问题是当你像这样注入(inject) View 模型时

class MyFragment: Fragment() {
@Inject lateinit var chartViewModel: ChartViewModel
dagger 只是创建了一个新的 viewmodel 实例。没有 viewmodel-fragment-lifecycle 魔法发生,因为这个 viewmodel 不在 Activity/fragment 的 viewmodelstore 中,也不是由您创建的 viewmodelfactory 提供。在这里,您可以将 View 模型视为真正的任何普通类。举个例子:
class MyFragment: Fragment() {
@Inject lateinit var anything: AnyClass
}
class AnyClass @Inject constructor(private val repository: ChartRepository) {
//live data code...
}
您的 View 模型相当于 AnyClass因为 viewmodel 不在 viewmodelstore 中,也不局限于 fragment/Activity 的生命周期。

Is there any way to achieve the same instance of viewmodel for multiple fragment and also at the same time inject the view model in fragments


不,因为上面列出的原因。

Also is there any need for the bindViewModelFactory method as it seems to have no effect on app even without this method.


它没有任何效果,因为(我假设)您没有使用 ViewModelFactory任何地方。由于它没有在任何地方引用,因此 viewmodelfactory 的这个 Dagger 代码是无用的。
@Binds
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

这是@binds 正在做的事情: 1 2
这就是为什么删除它对应用程序没有影响。
那么解决方案是什么?您需要将工厂注入(inject) fragment/Activity 并使用工厂获取 View 模型的实例
class MyFragment: Fragment() {
@Inject lateinit var viewModelFactory: ViewModelFactory

private val vm: ChartViewModel by lazy {
ViewModelProvider(X, YourViewModelFactory).get(ChartViewModel::class.java)
}
什么是 X这里? X 是 ViewModelStoreOwner .一个 ViewModelStoreOwner是在它们下面有 View 模型的东西。 ViewModelStoreOwner由activity和fragment实现。因此,您有几种创建 View 模型的方法:
  • Activity 中的 View 模型

  • ViewModelProvider(this, YourViewModelFactory)
  • fragment 中的 View 模型

  • ViewModelProvider(this, YourViewModelFactory)
  • fragment (B) 中的 View 模型作用于父 fragment (A) 并在 A 下的子 fragment 之间共享

  • ViewModelProvider(requireParentFragment(), YourViewModelFactory)
  • fragment 中的 View 模型限定为父 Activity ,并在 Activity
  • 下的 fragment 之间共享

    ViewModelProvider(requireActivity(), YourViewModelFactory)

    One workaround could be to make a BaseFragment for fragments which shares the common viewmodel, but that will again include the boilerplate code and also I am not a great fan of BaseFragment/BaseActivity


    是的,这确实是个坏主意。解决方法是使用 requireParentFragment()requireActivity()获取 View 模型实例。但是您将在每个具有 View 模型的 fragment/Activity 中编写相同的内容。为避免这种情况,您可以抽象出这个 ViewModelProvider(x, factory)参与基础 fragment/Activity 类,并在基类中注入(inject)工厂,这将简化您的子 fragment/Activity 代码,如下所示:
    class MyFragment: BaseFragment() {

    private val vm: ChartViewModel by bindViewModel() // or bindParentFragmentViewModel() or bindActivityViewModel()

    关于android - 使用 dagger2 在多个 fragment 中使用相同的 View 模型实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61972739/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com