如何在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
相关产品推荐
相关产品推荐

