You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法通过HiltViewModel构造函数传字符串:Dagger依赖缺失错误

Dagger 2迁移Hilt时ViewModel注入失败问题

问题背景

正在将Android项目从Dagger 2迁移到Hilt,大部分迁移过程顺利,但最后一个ViewModel转换时遇到注入错误。

ContactDetailViewModel代码

@HiltViewModel
class ContactDetailViewModel
@Inject
constructor(
    application: Application,
    contactId: String?,
    name: String?,
    refId: String,
) : NetworkBaseViewModel(application) {
    .... (非DI相关代码)

    @Inject
    lateinit var client: ApolloClient

    .... (非DI相关代码及构造参数引用)
}

ContactDetailVMFactory代码

@Singleton
class ContactDetailVMFactory @Inject constructor(
    val application: Application,
    val contactId: String?,
    val name: String?,
    val refId: String
) : ViewModelProvider.AndroidViewModelFactory(application) {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return ContactDetailViewModel(application, contactId, name, refId) as T
    }
}

ContactDetailEditFragment代码

@AndroidEntryPoint
open class ContactDetailEditFragment: Fragment() {

    val args: ContactDetailEditFragmentArgs by navArgs()

    open val vm by activityViewModels<ContactDetailViewModel>(
        factoryProducer = {
            ContactDetailVMFactory(
                    application = requireActivity().application,
                    contactId = args.contactId,
                    name = args.name,
                    refId = args.contactId ?: args.vmKey
            )
        },
        key = { args.vmKey }
    )
        ....

    @Inject
    lateinit var client: ApolloClient


        ....
}

错误构建输出

/BaseApplication_HiltComponents.java:141: error: [Dagger/MissingBinding] java.lang.String cannot be provided
without an @Inject constructor or an @Provides-annotated method.
public abstract static class SingletonC implements BaseApplication_GeneratedInjector,
                        ^
    java.lang.String is injected at
        com.companyname.contacts.vm.ContactDetailViewModel(…, name, …)
    com.companyname.contacts.vm.ContactDetailViewModel is injected at
        com.companyname.contacts.vm.ContactDetailViewModel_HiltModules.BindsModule.binds(vm)
    @dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject
.Provider<androidx.lifecycle.ViewModel>> is requested at
    dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint
.getHiltViewModelMap() [com.companyname.BaseApplication_HiltComponents.SingletonC → com.companyname
.BaseApplication_HiltComponents.ActivityRetainedC → com.companyname.BaseApplication_HiltComponents
.ViewModelC]
It is also requested at:
    com.companyname.contacts.vm.ContactDetailViewModel(…, contactId, …)
    com.companyname.contacts.vm.ContactDetailViewModel(…, refId)

疑问

已经研究多日,其他传递字符串的HiltViewModel都正常,怀疑是ViewModelFactory的问题。


解决方案

问题核心是**@HiltViewModel注解与自定义Factory的冲突**:

  1. 加了@HiltViewModel后,Hilt会自动生成注入逻辑,尝试编译期注入ViewModel构造参数。但contactId、name、refId是Fragment导航传来的运行时动态值,Hilt无法在编译期提供绑定,因此报错。
  2. 自定义Factory被标记为@Singleton,Hilt会尝试注入其构造参数,同样无法提供动态字符串,进一步触发错误。

正确改造步骤

步骤1:修改ViewModel,移除@HiltViewModel注解

改用自定义Factory传递动态参数,同时把ApolloClient移到构造参数中,避免ViewModel内部二次注入:

class ContactDetailViewModel
@Inject
constructor(
    application: Application,
    contactId: String?,
    name: String?,
    refId: String,
    val client: ApolloClient
) : NetworkBaseViewModel(application) {
    .... (非DI相关代码及构造参数引用)
}

步骤2:改造自定义Factory,使用@AssistedInject处理动态参数

用@AssistedInject区分静态依赖(由Hilt注入)和动态参数(运行时传入):

class ContactDetailVMFactory @AssistedInject constructor(
    private val application: Application,
    private val client: ApolloClient,
    @Assisted private val contactId: String?,
    @Assisted private val name: String?,
    @Assisted private val refId: String
) : ViewModelProvider.AndroidViewModelFactory(application) {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return ContactDetailViewModel(application, contactId, name, refId, client) as T
    }

    // 定义辅助工厂接口,用于在Fragment中传递动态参数
    @AssistedFactory
    interface Factory {
        fun create(contactId: String?, name: String?, refId: String): ContactDetailVMFactory
    }
}

步骤3:修改Fragment中的ViewModel获取逻辑

通过Hilt注入辅助工厂,再创建带动态参数的ViewModel Factory:

@AndroidEntryPoint
open class ContactDetailEditFragment: Fragment() {

    val args: ContactDetailEditFragmentArgs by navArgs()

    // 注入辅助工厂
    @Inject
    lateinit var vmFactory: ContactDetailVMFactory.Factory

    open val vm by activityViewModels<ContactDetailViewModel>(
        factoryProducer = {
            vmFactory.create(
                contactId = args.contactId,
                name = args.name,
                refId = args.contactId ?: args.vmKey
            )
        },
        key = { args.vmKey }
    )

    ....
}

其他ViewModel正常的原因

如果其他传递字符串的ViewModel没有问题,大概率是它们的字符串参数是编译期可提供的静态值(比如通过@Provides注解提供的常量),或者没有同时使用@HiltViewModel和自定义Factory,而是采用了@AssistedInject的正确方式处理动态参数。

内容的提问来源于stack exchange,提问作者petestmart

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 00:06:26