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

刷新时滑动RecyclerView触发IndexOutOfBoundsException崩溃如何解决?

问题分析与修复方案

问题诱因

这个Inconsistency detected报错是RecyclerView的典型错误,本质是Adapter持有的数据源、Adapter内部记录的条目位置,和你触发的更新通知三者不匹配,仅在滑动时崩溃是因为滑动过程中RecyclerView会频繁计算布局、预加载条目,会触发一致性校验,静止时不会执行校验所以不会报错。具体到你的代码有以下几个直接诱因:

  • 通知更新的位置完全错误:你传入的id是分页的页码,不是RecyclerView的条目位置。你按每页10条存储,第id个分页对应RecyclerView里的第id*10到id*10+9个条目,直接调用notifyItemChanged(id)相当于通知完全错误的位置更新,自然会出现位置不匹配的问题。
  • 数据更新和通知时机不同步:你调用_articles.postValue()是异步将数据同步到主线程,但是紧接着就同步调用了Adapter的notify方法,此时Adapter可能还没拿到最新的数据源,用旧数据匹配新的更新通知就会出现不一致。
  • 直接修改LiveData持有的列表引用:你直接修改_articles.value对应的ArrayList实例,没有生成新的列表,滑动过程中如果Adapter正在遍历数据源,同时你在后台修改列表内容,就会出现并发修改导致的索引异常。
  • ViewModel持有Adapter引用违反分层原则:ViewModel生命周期长于View层,持有Adapter会导致内存泄漏,也会破坏数据更新和UI通知的时序一致性。
  • 下拉刷新清空旧数据时没有同步通知Adapter:如果你触发重载时清空了旧的分页数据,但是没有调用对应的删除通知,Adapter还以为条目总数是旧的数值,加载新数据时自然会出现索引越界。

修复方案

  1. 调整通知逻辑,匹配正确的条目位置
    如果你是将所有分页的文章平铺展示,更新第id个分页后,应该通知对应范围的条目更新:
// 仅在View层拿到新数据后调用,不要写在ViewModel里
recyclerAdapter.notifyItemRangeChanged(id * 10, 10)

如果你是分页作为分组展示,也要计算分页对应的实际条目位置再触发通知。
2. 移除ViewModel中的Adapter引用,遵循MVVM分层逻辑
ViewModel只负责更新LiveData数据,不要持有任何View层的引用:

// 修改后的ViewModel方法
fun setArticlePage(id: Int, content: ThomsLineWordpressArticlePage) {
    if (_articles.value == null || id >= _articles.value!!.size || (id < _articles.value!!.size && !_articles.value!![id].equals(content))) {
        // 生成新的列表实例,避免并发修改
        val newList = _articles.value?.toMutableList() ?: mutableListOf()
        if (newList.size <= id) {
            newList.add(content)
        } else {
            newList[id] = content
        }
        // 主线程更新用setValue,后台线程更新用postValue
        _articles.postValue(newList)
    }
}

然后在View层(Activity/Fragment)观察LiveData变化,更新Adapter数据源后再触发通知:

viewModel.articles.observe(this) { newPageList ->
    adapter.updateData(newPageList) // 先更新Adapter持有的数据源
    // 再触发对应范围的更新通知,如果你做了差量计算可以只更新变化的分页
    adapter.notifyDataSetChanged() // 前期可以先用全量通知保证稳定,后续再优化为局部更新
}
  1. 重载时同步清空数据源和通知
    触发下拉刷新时,先清空LiveData的旧数据,同时通知Adapter条目删除:
// ViewModel里添加重置方法
fun resetArticles() {
    _articles.postValue(mutableListOf())
}

// View层监听重载事件时调用
swipeRefreshLayout.setOnRefreshListener {
    viewModel.resetArticles()
    // 再触发分页重新加载
    viewModel.loadFirstPage()
}
  1. 兜底方案
    如果临时要快速定位问题,可以先把局部通知替换为notifyDataSetChanged(),确认崩溃消失后再逐步优化为局部更新,缩小问题范围。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 23:15:03