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

Dagger Hilt:如何将ViewModel注入Adapter?

解决Hilt注入ViewModel到Adapter的问题

嘿,我明白你现在遇到的问题了——在Fragment里用Hilt注入ViewModel顺风顺水,但到Adapter这儿就各种报错,不管是用by viewModels()还是构造/字段注入都搞不定。这其实是因为Adapter和ViewModel的生命周期绑定逻辑不兼容,咱们一步一步来解决它~

为什么会出错?

先搞清楚根源:

  1. by viewModels()是Fragment/Activity专属的扩展函数,它依赖这些组件的ViewModelStoreOwner身份来创建和管理ViewModel的生命周期,Adapter不属于这类生命周期组件,所以自然没法用这个方法。
  2. Hilt的@ViewModelInject是为了让Hilt在ViewModelProvider的体系下创建ViewModel,而ViewModel的作用域是和Fragment/Activity绑定的。Adapter不在这个作用域里,Dagger找不到对应的绑定关系,就会抛出MissingBinding错误。

而且从架构设计来说,Adapter本来就应该只负责UI渲染和简单的交互转发,直接持有ViewModel会让它和业务逻辑耦合,还容易引发内存泄漏,所以咱们换个更合理的方式来做。

最佳解决方案:通过回调让Fragment做中间层

核心思路是:Adapter只负责传递用户交互事件给Fragment,Fragment再调用ViewModel的方法;ViewModel的数据变化由Fragment观察,再同步给Adapter。这样既解耦,又符合MVVM的设计原则。

步骤1:定义交互回调接口

先写一个接口,用来让Adapter把用户操作传递给Fragment:

interface HomeListInteractionListener {
    fun onUserItemClicked(user: Users)
    // 可以根据需求添加更多回调,比如长按、按钮点击等
}

步骤2:修改Adapter,接收回调并移除ViewModel依赖

把Adapter里的ViewModel依赖换成回调接口,注入的时候传入这个接口的实现:

class HomeListAdapter @Inject constructor(
    private val interactionListener: HomeListInteractionListener
): BaseListAdapter<Users>(
    itemsSame = { old, new -> old.username == new.username },
    contentsSame = { old, new -> old == new }
) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<Users> {
        // 创建ViewHolder的逻辑不变
    }

    override fun onBindViewHolder(holder: BaseViewHolder<Users>, position: Int) {
        val user = getItem(position)
        holder.bind(user)
        // 比如点击item时触发回调
        holder.itemView.setOnClickListener {
            interactionListener.onUserItemClicked(user)
        }
    }
}

步骤3:在Fragment中实现回调并连接ViewModel

让Fragment实现刚才的回调接口,同时观察ViewModel的数据来更新Adapter:

@AndroidEntryPoint
class HomeFragment : BaseFragment<FragmentHomeBinding, HomeViewModel>(R.layout.fragment_home), HomeListInteractionListener {
    private val viewModel: HomeViewModel by viewModels()
    @Inject lateinit var homeListAdapter: HomeListAdapter

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 给RecyclerView设置Adapter
        binding.recyclerView.adapter = homeListAdapter

        // 观察ViewModel中的用户数据,更新Adapter列表
        viewModel.users.observe(viewLifecycleOwner) { userList ->
            homeListAdapter.submitList(userList)
        }
    }

    // 实现回调接口,在这里调用ViewModel的业务方法
    override fun onUserItemClicked(user: Users) {
        viewModel.handleUserClick(user)
        // 比如ViewModel里的handleUserClick方法处理点击逻辑,比如跳转到详情、更新数据等
    }
}

特殊情况:一定要在Adapter里访问ViewModel?

如果因为某些特殊需求,必须在Adapter里获取ViewModel的实例(非常不推荐,尽量避免),可以通过Fragment传递ViewModel的弱引用,防止内存泄漏:

修改Adapter

class HomeListAdapter @Inject constructor(): BaseListAdapter<Users>(
    itemsSame = { old, new -> old.username == new.username },
    contentsSame = { old, new -> old == new }
) {
    private var viewModelRef: WeakReference<HomeViewModel>? = null

    fun attachViewModel(viewModel: HomeViewModel) {
        viewModelRef = WeakReference(viewModel)
    }

    // 使用时先检查引用是否有效
    private fun doSomethingWithViewModel() {
        viewModelRef?.get()?.let { viewModel ->
            // 在这里访问ViewModel的方法或数据
            val someData = viewModel.someLiveData.value
        }
    }
}

在Fragment中传递ViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    homeListAdapter.attachViewModel(viewModel)
}

总结

尽量遵循Adapter只做UI相关的事的原则,通过回调让Fragment作为Adapter和ViewModel的桥梁,这样不仅能解决Hilt注入的问题,还能让代码结构更清晰,避免生命周期相关的坑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 16:17:31