Mvvm 在使用Dagger时,我们真的需要ViewModelFactorys和viewmodelProviders吗?

Mvvm 在使用Dagger时,我们真的需要ViewModelFactorys和viewmodelProviders吗?,mvvm,dagger-2,dagger,android-viewmodel,android-mvvm,Mvvm,Dagger 2,Dagger,Android Viewmodel,Android Mvvm,所以我正在使用Dagger进行一些MVVM示例项目。我有一个viewmodel工厂,如下所示: class DaggerViewModelFactory @Inject constructor(private val viewModelsMap: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {

所以我正在使用Dagger进行一些MVVM示例项目。我有一个viewmodel工厂,如下所示:

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)
        }
    }
}
@PerActivity
@Subcomponent(modules = [ActivityModule::class, ViewModelFactoryModule::class, MyViewModelModule::class])
interface ActivityComponent {
    fun inject(mainActivity: MainActivity)
}
class TakePicturesViewModel @Inject constructor(app: Application): AndroidViewModel(app) {...
    @Inject
    lateinit var viewModelFactory: DaggerViewModelFactory
private lateinit var takePicturesViewModel: TakePicturesViewModel
.
.
.
    takePicturesViewModel = ViewModelProviders.of(this, viewModelFactory).get(TakePicturesViewModel::class.java)
@Inject
lateinit var takePicturesViewModel: TakePicturesViewModel
我得到了一个ViewModelModule:

@Module
abstract class MyViewModelModule {
    @Binds
    @IntoMap
    @ViewModelKey(TakePicturesViewModel::class)
    abstract fun bindTakePictureViewModel(takePicturesViewModel: TakePicturesViewModel): ViewModel
}
一个如下所示的组件:

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)
        }
    }
}
@PerActivity
@Subcomponent(modules = [ActivityModule::class, ViewModelFactoryModule::class, MyViewModelModule::class])
interface ActivityComponent {
    fun inject(mainActivity: MainActivity)
}
class TakePicturesViewModel @Inject constructor(app: Application): AndroidViewModel(app) {...
    @Inject
    lateinit var viewModelFactory: DaggerViewModelFactory
private lateinit var takePicturesViewModel: TakePicturesViewModel
.
.
.
    takePicturesViewModel = ViewModelProviders.of(this, viewModelFactory).get(TakePicturesViewModel::class.java)
@Inject
lateinit var takePicturesViewModel: TakePicturesViewModel
viewmodel的显示方式如下:

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)
        }
    }
}
@PerActivity
@Subcomponent(modules = [ActivityModule::class, ViewModelFactoryModule::class, MyViewModelModule::class])
interface ActivityComponent {
    fun inject(mainActivity: MainActivity)
}
class TakePicturesViewModel @Inject constructor(app: Application): AndroidViewModel(app) {...
    @Inject
    lateinit var viewModelFactory: DaggerViewModelFactory
private lateinit var takePicturesViewModel: TakePicturesViewModel
.
.
.
    takePicturesViewModel = ViewModelProviders.of(this, viewModelFactory).get(TakePicturesViewModel::class.java)
@Inject
lateinit var takePicturesViewModel: TakePicturesViewModel
因此,我可以使用视图模型工厂将我的viewmodel注入到我的活动中,如下所示:

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)
        }
    }
}
@PerActivity
@Subcomponent(modules = [ActivityModule::class, ViewModelFactoryModule::class, MyViewModelModule::class])
interface ActivityComponent {
    fun inject(mainActivity: MainActivity)
}
class TakePicturesViewModel @Inject constructor(app: Application): AndroidViewModel(app) {...
    @Inject
    lateinit var viewModelFactory: DaggerViewModelFactory
private lateinit var takePicturesViewModel: TakePicturesViewModel
.
.
.
    takePicturesViewModel = ViewModelProviders.of(this, viewModelFactory).get(TakePicturesViewModel::class.java)
@Inject
lateinit var takePicturesViewModel: TakePicturesViewModel
或者根本不使用viewmodel factory,如下所示:

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)
        }
    }
}
@PerActivity
@Subcomponent(modules = [ActivityModule::class, ViewModelFactoryModule::class, MyViewModelModule::class])
interface ActivityComponent {
    fun inject(mainActivity: MainActivity)
}
class TakePicturesViewModel @Inject constructor(app: Application): AndroidViewModel(app) {...
    @Inject
    lateinit var viewModelFactory: DaggerViewModelFactory
private lateinit var takePicturesViewModel: TakePicturesViewModel
.
.
.
    takePicturesViewModel = ViewModelProviders.of(this, viewModelFactory).get(TakePicturesViewModel::class.java)
@Inject
lateinit var takePicturesViewModel: TakePicturesViewModel
这两种方法都有效,所以我想知道哪种方法是正确的,如果使用Dagger允许我在不需要viewmodelfactory的情况下注入viewmodel,是否有充分的理由保留它?还是我应该放弃这个viewmodelfactory

提前谢谢你的建议

问候

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

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

Dagger可以创建ViewModel,这是您在通用ViewModelFactory中使用的。这些视图模型应该是无范围的,因此每次都要创建一个新的视图模型。Android支持库将缓存该ViewModel,并在旋转后重新使用它,这样您就可以在调用factory方法一次后保留数据,并且将只创建一个ViewModel(每个生命周期)。您保留了数据,一切都按预期进行

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

当然,您可以将范围应用于ViewModel,但该范围应比活动实例的寿命长(以保持旋转之间的状态),但不会比屏幕可见的时间长。因此,您既不能将其范围扩展到活动,也不能扩展到应用程序生命周期。您可以通过在两者之间引入一个新的作用域来实现它,但此时您将重新创建ViewModel库


tl;dr插入并使用工厂,否则您将得到一个混乱/错误的ViewModel实现

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

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

Dagger可以创建ViewModel,这是您在通用ViewModelFactory中使用的。这些视图模型应该是无范围的,因此每次都要创建一个新的视图模型。Android支持库将缓存该ViewModel,并在旋转后重新使用它,这样您就可以在调用factory方法一次后保留数据,并且将只创建一个ViewModel(每个生命周期)。您保留了数据,一切都按预期进行

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

当然,您可以将范围应用于ViewModel,但该范围应比活动实例的寿命长(以保持旋转之间的状态),但不会比屏幕可见的时间长。因此,您既不能将其范围扩展到活动,也不能扩展到应用程序生命周期。您可以通过在两者之间引入一个新的作用域来实现它,但此时您将重新创建ViewModel库



tl;dr插入并使用工厂,否则您将得到一个混乱/错误的ViewModel实现。

您需要
ViewModelProvider(这个工厂)。get(vmClass)
但不一定需要多重绑定。请参见您需要的
ViewModelProvider(这个工厂).get(vmClass)
,但不一定需要多重绑定。随着Hilt的介绍,现在您可以在视图模型中使用@ActivityRetainedScope,它在活动旋转时为您提供相同的实例。只要您使用的是
androidx.ViewModel
,我就坚持使用他们的工厂/方法创建虚拟机等等。不管怎样,把事情搞混只会让人困惑,也很难维持。如果你想创建自己的虚拟机,当然可以。我只是说,如果你想做自己的事情,就不要使用
androidx.ViewModel
。随着Hilt的引入,现在你可以使用@ActivityRetainedScope作为你的视图模型,这在活动轮换时为您提供了相同的实例。只要您使用的是
androidx.ViewModel
,我就坚持使用他们的工厂/方法来创建虚拟机等等。不管怎样,把事情搞混只会让人困惑,也很难维持。如果你想创建自己的虚拟机,当然可以。我只是说如果你想做你自己的事情,就不要使用
androidx.ViewModel