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

如何在RecyclerView Adapter中使用ViewModel及LiveData实现网络操作

现有代码存在的核心问题
  • 直接在Adapter中持有Activity强引用,页面销毁时如果Adapter仍有异步任务在执行,会触发内存泄漏
  • 在Adapter内部自行初始化ViewModel是错误的用法:ViewModel的作用域绑定到Activity/Fragment,应该由宿主(Activity/Fragment)初始化后注入到Adapter,而非Adapter自己创建
  • 给LiveData注册观察者时直接用Activity作为LifecycleOwner,页面重建时会出现重复订阅、回调异常的问题

正确实现方案

步骤1:宿主层初始化ViewModel并注入Adapter

在Activity/Fragment中完成ViewModel的初始化,创建Adapter时将ViewModel、生命周期所有者、必要的业务回调注入,避免Adapter直接持有宿主实例:

// Activity 内代码
private val viewModel: CartAdapterViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // 初始化Adapter,传入必要参数
    val adapter = CartAdapter(
        cartList = yourCartList,
        viewModel = viewModel,
        lifecycleOwner = this,
        onLoadingStateChange = { isShow ->
            if (isShow) showLoader() else hideLoader()
        }
    )
    binding.recyclerView.adapter = adapter
}

步骤2:改造Adapter结构,移除Activity强引用

所有需要宿主执行的逻辑都通过函数回调实现,用传入的LifecycleOwner来观察LiveData,自动管理订阅生命周期:

class CartAdapter(
    private val cartList: ArrayList<ProductData>,
    private val viewModel: CartAdapterViewModel,
    private val lifecycleOwner: LifecycleOwner,
    private val onLoadingStateChange: (Boolean) -> Unit
) : RecyclerView.Adapter<CartAdapter.MyViewHolder>() {

    init {
        initObserver()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.cart_item_layout, parent, false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val item = cartList[position]
        holder.setData(item)
        // 示例:点击修改商品数量触发网络请求
        holder.binding?.btnModifyCount?.setOnClickListener {
            onLoadingStateChange.invoke(true)
            viewModel.modifyProductCount(item.id, holder.binding?.etCount?.text.toString())
        }
    }

    override fun getItemCount(): Int = cartList.size

    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val binding: CartItemLayoutBinding? = DataBindingUtil.bind(itemView)

        fun setData(model: ProductData) {
            binding?.item = model
            // 给Binding设置生命周期所有者,保证LiveData变化能及时更新UI
            binding?.lifecycleOwner = lifecycleOwner
        }
    }

    private fun initObserver() {
        viewModel.statusResponse.observe(lifecycleOwner) {
            onLoadingStateChange.invoke(false)
            // 处理请求成功逻辑,比如更新对应item数据
            notifyDataSetChanged()
        }

        viewModel.serverError.observe(lifecycleOwner) {
            onLoadingStateChange.invoke(false)
            // 错误提示可通过回调交给宿主处理
        }
    }
}

步骤3:列表项粒度请求适配

如果需要每个列表项单独处理自己的网络请求(比如单个商品点赞、收藏),不要用全局的ViewModel LiveData,给每个ProductData包装对应的请求状态字段,或者用SavedStateHandle按itemId存储状态,避免不同item的回调混淆。


注意事项
  • 禁止在Adapter中持有Activity/Fragment实例,所有需要宿主处理的逻辑(加载框、Toast、页面跳转)都通过函数回调实现
  • 若列表存在大量item,避免在onBindViewHolder中重复注册观察者,所有全局观察统一放在Adapter的init块中执行
  • DataBinding绑定的Item布局如果用到LiveData,必须给binding设置lifecycleOwner,否则数据变化不会触发UI更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 23:24:05