[英]Hilt - How to inject ViewModel interface?
根据 Hilt 教程,ViewModels 需要通过以下方式注入:
@HiltViewModel
class ExampleViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
private val repository: ExampleRepository
) : ViewModel() {
...
}
但是,就我而言,我想使用一个界面:
interface ExampleViewModel()
@HiltViewModel
class ExampleViewModelImp @Inject constructor(
private val savedStateHandle: SavedStateHandle,
private val repository: ExampleRepository
) : ExampleViewModel, ViewModel() {
...
}
然后我想通过接口注入
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
private val exampleViewModel: ExampleViewModel by viewModels()
...
}
如何使这项工作?
viewModels
需要ViewModel
的孩子 class
val viewModel: ExampleViewModel by viewModels<ExampleViewModelImp>()
有一个类似的问题,我想通过接口注入 ViewModel,主要是因为在测试时用假实现切换它。 我们正在从 Dagger Android 迁移到 Hilt,并且我们进行了使用假视图模型的 UI 测试。 在这里添加我的发现,以便它可以帮助面临类似问题的人。
by viewModels()
和ViewModelProviders.of(...)
都需要一个扩展ViewModel()
的类型。 所以接口将不可能,但我们仍然可以使用扩展ViewModel()
的抽象 class@HiltViewModel
,因为无法切换实现。Fragment
中注入ViewModelFactory
。 您可以在测试期间切换工厂,从而切换 ViewModel。@AndroidEntryPoint
class ListFragment : Fragment() {
@ListFragmentQualifier
@Inject
lateinit var factory: AbstractSavedStateViewModelFactory
private val viewModel: ListViewModel by viewModels(
factoryProducer = { factory }
)
}
abstract class ListViewModel : ViewModel() {
abstract fun load()
abstract val title: LiveData<String>
}
class ListViewModelImpl(
private val savedStateHandle: SavedStateHandle
) : ListViewModel() {
override val title: MutableLiveData<String> = MutableLiveData()
override fun load() {
title.value = "Actual Implementation"
}
}
class ListViewModelFactory(
owner: SavedStateRegistryOwner,
args: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, args) {
override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
return ListViewModelImpl(handle) as T
}
}
@Module
@InstallIn(FragmentComponent::class)
object ListDI {
@ListFragmentQualifier
@Provides
fun provideFactory(fragment: Fragment): AbstractSavedStateViewModelFactory {
return ListViewModelFactory(fragment, fragment.arguments)
}
}
@Qualifier
annotation class ListFragmentQualifier
在这里, ListViewModel
是抽象的 class 而ListViewModelImpl
是实际的实现。 您可以在使用TestInstallIn
进行测试时切换ListDI
模块。 有关这方面的更多信息和工作项目,请参阅本文
找到了一个解决方案,使用HiltViewModel
作为我希望注入的实际 class 的代理。 它很简单,而且很有魅力;)
@Module
@InstallIn(ViewModelComponent::class)
object MyClassModule{
@Provides
fun provideMyClas(): MyClass = MyClassImp()
}
class MyClassImp : MyClass {
// your magic goes here
}
@HiltViewModel
class Proxy @Inject constructor(val ref: MyClass) : ViewModel()
@AndroidEntryPoint
class MyFragment : Fragment() {
private val myClass by lazy {
val viewModel by viewModels<Proxy>()
viewModel.ref
}
}
现在你得到了MyClass
接口类型的myClass
绑定到viewModels<Proxy>()
lifeCycle
注入一个接口就是这么简单,你传递一个接口但是注入注入一个Impl。
@InstallIn(ViewModelComponent::class)
@Module
class DIModule {
@Provides
fun providesRepository(): YourRepository = YourRepositoryImpl()
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.