首页 > 解决方案 > 如何使用 Dagger Hilt 在动态功能模块中创建 ViewModel?

问题描述

尝试在动态功能模块中创建 ViewModel private val viewModel: PostDetailViewModel by viewModels()

在片段中

class PostDetailFragment : DynamicNavigationFragment<FragmentPostDetailBinding>() {

    private val viewModel: PostDetailViewModel by viewModels()
    
    override fun getLayoutRes(): Int = R.layout.fragment_post_detail

    override fun bindViews() {
        // Get Post from navigation component arguments
        val post = arguments?.get("post") as Post
        dataBinding.item = post
        viewModel.updatePostStatus(post)
        
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        initCoreDependentInjection()
        super.onCreate(savedInstanceState)
    }

    private fun initCoreDependentInjection() {

        val coreModuleDependencies = EntryPointAccessors.fromApplication(
            requireActivity().applicationContext,
            DomainModuleDependencies::class.java
        )

        DaggerPostDetailComponent.factory().create(
            coreModuleDependencies,
            requireActivity().application
        )
            .inject(this)

    }
}

结果错误

Caused by: java.lang.InstantiationException: java.lang.Class<com.x.post_detail.PostDetailViewModel> has no zero argument constructor

它适用于应用程序模块中的任何片段,但不适用于动态功能模块。将 ViewModel 添加到动态功能模块的正确方法是什么?我应该使用 ViewModelFactory 在 app 模块中创建 ViewModel 并从 app 模块中获取它们吗?

标签: androidandroid-viewmodeldagger-hiltdynamic-feature-module

解决方案


基于这个官方github帖子

现在在https://developer.android.com/training/dependency-injection/hilt-multi-module#dfm有关于 Hilt 和 DFM 的文档

但总的来说,由于我们是由子组件和整体组件构建的,因此您将无法将标准 Hilt 机制(如 @AndroidEntryPoint 与 DFM)一起使用。

抱歉不行。@ViewModelInject 使用整体的 Hilt ActivityRetainedComponent,因此您的 DFM 中的任何 @ViewModelInject 类都不会被识别。

目前看来,仅使用动态功能模块@ViewModelInjectby viewModels()在动态功能模块中注入 ViewModel 似乎是不可能的。

基于格子应用程序,我在动态功能模块中重建了我的 Dagger 模块

@InstallIn(FragmentComponent::class)
@Module
class PostDetailModule {

    @Provides
    fun providePostDetailViewModel(fragment: Fragment, factory: PostDetailViewModelFactory) =
        ViewModelProvider(fragment, factory).get(PostDetailViewModel::class.java)

    @Provides
    fun provideCoroutineScope() = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob())

}

而 ViewModel 和 ViewModelFactory 是

class PostDetailViewModel @ViewModelInject constructor(
    private val coroutineScope: CoroutineScope,
    private val getPostsUseCase: UseCase
) : ViewModel() {
 
    // Do other things
}

class PostDetailViewModelFactory @Inject constructor(
    private val coroutineScope: CoroutineScope,
    private val getPostsUseCase: UseCase
) : ViewModelProvider.Factory {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass != PostDetailViewModel::class.java) {
            throw IllegalArgumentException("Unknown ViewModel class")
        }
        return PostDetailViewModel(
            coroutineScope,
            getPostsUseCase
        ) as T
    }
}

并注入到动态特征模块中的片段

class PostDetailFragment : Fragment() {

    @Inject
    lateinit var viewModel: PostDetailViewModel


    override fun onCreate(savedInstanceState: Bundle?) {
        initCoreDependentInjection()
        super.onCreate(savedInstanceState)
    }

    private fun initCoreDependentInjection() {

        val coreModuleDependencies = EntryPointAccessors.fromApplication(
            requireActivity().applicationContext,
            DomainModuleDependencies::class.java
        )

        DaggerPostDetailComponent.factory().create(
            dependentModule = coreModuleDependencies,
            fragment = this
        )
            .inject(this)
    }
}

推荐阅读