如何使用Dagger2为抽象ViewModel类注入构造函数?
问题:将@Inject移到抽象ViewModel类并仅绑定抽象类是否可行?
我有一个抽象ViewModel类:
abstract class AbstractViewModel( protected val dataStore: DataStore, protected val schedulerProvider: SchedulerProvider ): ViewModel() { abstract fun doSomething() }
多个ViewModel继承自该抽象类:
class InheritedViewModel1 @Inject constructor( dataStore: DataStore, schedulerProvider: SchedulerProvider ) : AbstractViewModel(dataStore, schedulerProvider) { override fun doSomething() {} } class InheritedViewModel2 @Inject constructor( dataStore: DataStore, schedulerProvider: SchedulerProvider ) : AbstractViewModel(dataStore, schedulerProvider) { override fun doSomething() {} }
当前实现可正常运行,但每个子类ViewModel都需在Dagger中单独绑定:
@Binds @IntoMap @ViewModelKey(InheritedViewModel1::class) abstract fun bindInheritedViewModel1(inheritedViewModel1: InheritedViewModel1): ViewModel @Binds @IntoMap @ViewModelKey(InheritedViewModel2::class) abstract fun bindInheritedViewModel2(inheritedViewModel2: InheritedViewModel2): ViewModel
我想将@Inject注解移到抽象类中,仅绑定抽象类:
abstract class AbstractViewModel @Inject constructor( protected val dataStore: DataStore, protected val schedulerProvider: SchedulerProvider ): ViewModel() { abstract fun doSomething() } @Binds @IntoMap @ViewModelKey(AbstractViewModel::class) abstract fun bindAbstractViewModel(abstractViewModel: AbstractViewModel): ViewModel
然后移除所有子类的注入配置,这种方式是否可行?若可行,该如何实例化子类?
答案
这种方式不可行,核心原因有两点:
- 抽象类无法被实例化:Dagger依赖注入的本质是创建具体类的实例,抽象类没有可执行的构造实现,Dagger无法生成它的实例对象。
- 绑定不匹配实际需求:业务代码中需要的是
InheritedViewModel1/InheritedViewModel2这类具体子类的实例,但仅绑定AbstractViewModel的话,Dagger的ViewModel Map中只会存在抽象类的key,无法根据具体子类的类型找到对应的实例。
优化方案:简化子类的绑定代码(减少重复书写)
你不需要移除子类的绑定,而是可以通过复用逻辑来减少重复的注解代码:
方法1:抽象模块类复用绑定逻辑
创建一个通用的抽象Dagger模块,定义ViewModel的绑定模板,子类模块只需继承该抽象类即可:
// 抽象模板模块 abstract class BaseViewModelModule<T : AbstractViewModel> { @Binds @IntoMap @ViewModelKey(T::class) abstract fun bindViewModel(viewModel: T): ViewModel } // 子类模块(仅需继承,无需重复写注解) @Module abstract class InheritedViewModel1Module : BaseViewModelModule<InheritedViewModel1>() @Module abstract class InheritedViewModel2Module : BaseViewModelModule<InheritedViewModel2>()
方法2:简化子类的构造注入(可选)
如果将@Inject移到抽象类的构造上,子类可以省略构造参数的声明,直接继承父类的注入逻辑,减少子类的代码量:
abstract class AbstractViewModel @Inject constructor( protected val dataStore: DataStore, protected val schedulerProvider: SchedulerProvider ): ViewModel() { abstract fun doSomething() } // 子类无需再声明构造参数,自动继承父类的注入逻辑 class InheritedViewModel1 : AbstractViewModel() { override fun doSomething() {} } class InheritedViewModel2 : AbstractViewModel() { override fun doSomething() {} }
但注意:即使这样,你仍然需要为每个子类添加Dagger绑定(可以用上面的抽象模块方式简化),因为Dagger需要知道具体子类的存在才能生成实例。
内容的提问来源于stack exchange,提问作者Mehdi Satei
相关产品推荐
相关产品推荐

