使用 Dagger 时我们真的需要 viewModelFactories 和 viewmodelProviders 吗?

lin*_*r85 1 mvvm dagger dagger-2 android-mvvm android-viewmodel

所以我正在使用 Dagger 开发一些示例 MVVM 项目。我有一个视图模型工厂,如下所示:

class DaggerViewModelFactory @Inject constructor(private val viewModelsMap: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val creator = viewModelsMap[modelClass] ?:
        viewModelsMap.asIterable().firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
        return try {
            creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

视图模型工厂模块

@Module
abstract class ViewModelFactoryModule {
    @Binds
    abstract fun bindViewModelFactory(viewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
}
Run Code Online (Sandbox Code Playgroud)

我有一个 ViewModelModule:

@Module
abstract class MyViewModelModule {
    @Binds
    @IntoMap
    @ViewModelKey(TakePicturesViewModel::class)
    abstract fun bindTakePictureViewModel(takePicturesViewModel: TakePicturesViewModel): ViewModel
}
Run Code Online (Sandbox Code Playgroud)

一个像这样的组件:

@PerActivity
@Subcomponent(modules = [ActivityModule::class, ViewModelFactoryModule::class, MyViewModelModule::class])
interface ActivityComponent {
    fun inject(mainActivity: MainActivity)
}
Run Code Online (Sandbox Code Playgroud)

一个视图模型如下所示:

class TakePicturesViewModel @Inject constructor(app: Application): AndroidViewModel(app) {...
Run Code Online (Sandbox Code Playgroud)

因此,我可以使用视图模型工厂将视图模型注入到我的活动中,如下所示:

    @Inject
    lateinit var viewModelFactory: DaggerViewModelFactory
private lateinit var takePicturesViewModel: TakePicturesViewModel
.
.
.
    takePicturesViewModel = ViewModelProviders.of(this, viewModelFactory).get(TakePicturesViewModel::class.java)
Run Code Online (Sandbox Code Playgroud)

或者根本没有视图模型工厂,如下所示:

@Inject
lateinit var takePicturesViewModel: TakePicturesViewModel
Run Code Online (Sandbox Code Playgroud)

两种方法都有效,所以我想知道哪一种是正确的工作方法,如果使用 Dagger 允许我在不需要 viewmodelfactory 的情况下注入 viewmodel,是否有充分的理由保留它?或者我应该摆脱这个 viewmodelfactory ?

预先感谢您的任何建议。

问候

Dav*_*jak 8

两种方法都有效,所以我想知道哪一种是正确的工作方法,如果使用 Dagger 允许我在不需要 viewmodelfactory 的情况下注入 viewmodel,是否有充分的理由保留它?或者我应该摆脱这个 viewmodelfactory ?

两种方式的工作方式不同。尝试使用 ViewModel 中存储的数据旋转屏幕,您就会看到。

Dagger 可以创建 ViewModel,这就是您在通用 ViewModelFactory 中使用的内容。这些视图模型应该是无作用域的,因此您每次都会创建一个新的 ViewModel。Android 支持库将缓存该 ViewModel 并在旋转后重用它,以便您可以保留数据 - 工厂方法被调用一次,并且只会创建一个 ViewModel(每个生命周期)。您保留数据,一切都会按预期运行。

另一方面,如果您使用 Dagger 直接注入 ViewModel,则上述情况均不适用。与任何其他依赖项一样,新的 ViewModel 将在创建时注入,导致每次使用时都会创建一个 ViewModel — 您不仅会使用其中存储的数据,而且无法与片段共享状态任何一个。

当然,您可以将作用域应用于 ViewModel,但该作用域的寿命应该比 Activity 实例的寿命更长(以保持旋转之间的状态),但寿命不能长于屏幕可见。因此,您既不能将其范围限定为活动,也不能限定为应用程序生命周期。您可以通过在中间引入一个新的作用域来使其工作,但此时您将重新发明 ViewModel 库。


tl;dr注入并使用工厂,否则您将得到一个令人困惑/错误的 ViewModel 实现。