繁体   English   中英

Hilt - 如何注入 ViewModel 接口?

[英]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 测试。 在这里添加我的发现,以便它可以帮助面临类似问题的人。

  1. by viewModels()ViewModelProviders.of(...)都需要一个扩展ViewModel()的类型。 所以接口将不可能,但我们仍然可以使用扩展ViewModel()的抽象 class
  2. 我认为没有办法为此目的使用@HiltViewModel ,因为无法切换实现。
  3. 因此,尝试在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM