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

启动MainActivity触发IndexOutOfBoundsException异常求助排查

问题根源与修复方案

核心问题

你错误地将PagingDataAdapter当作普通RecyclerView.Adapter使用:

  • PagingDataAdapter本身内置了分页数据的管理逻辑,不需要手动维护listStories集合
  • 你重写的getItemCount返回自定义列表长度,但Paging库内部逻辑可能在列表为空时仍尝试绑定视图,导致访问listStories[position]触发越界异常

错误触发逻辑

当listStories为空(size=0),但Paging库或RecyclerView的缓存机制触发了onBindViewHolder调用(比如旧视图复用、分页预加载逻辑误判),此时position=0访问空列表就会抛出IndexOutOfBoundsException。

修复步骤

1. 移除自定义数据集合与方法

删除listStories变量和setListStories方法,改用PagingDataAdapter标准API管理数据。

2. 取消重写getItemCount

PagingDataAdapter会自动根据分页数据返回正确条目数,无需手动实现。

3. 修正onBindViewHolder逻辑

通过getItem(position)获取分页数据,同时处理空值(getItem可能返回null):

4. 修复DiffUtil实现

原areItemsTheSame用对象相等判断错误,应使用唯一标识(如id)判断条目是否为同一个:

修改后的完整代码

class MainAdapter: PagingDataAdapter<ListStoryItem, MainAdapter.MyViewHolder>(DIFF_CALLBACK) {

    private lateinit var onItemClickCallback: OnItemClickCallback

    class MyViewHolder(var binding: StoryItemBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val binding = StoryItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return MyViewHolder(binding)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val story = getItem(position) ?: return // 空值直接返回,避免崩溃
        
        val (photoUrl, createdAt, name, description, _) = story
        holder.binding.tvName.text = name
        holder.binding.tvStory.text = description
        
        val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault())
        val outputFormat = SimpleDateFormat("MMM dd, yyyy - hh:mm a", Locale.getDefault())
        val date = inputFormat.parse(createdAt)
        val formattedDateTimeString = outputFormat.format(date)

        holder.binding.tvDate.text = formattedDateTimeString
        
        Glide.with(holder.itemView.context)
            .load(photoUrl)
            .into(holder.binding.imageView2)
            
        holder.binding.imageView2.setOnClickListener{
            onItemClickCallback.onItemClicked(story)
        }
    }

    fun setOnItemClickCallback(onItemClickCallback: OnItemClickCallback) {
        this.onItemClickCallback = onItemClickCallback
    }

    interface OnItemClickCallback {
        fun onItemClicked(data: ListStoryItem)
    }

    companion object {
        private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<ListStoryItem>() {
            override fun areItemsTheSame(oldItem: ListStoryItem, newItem: ListStoryItem): Boolean {
                return oldItem.id == newItem.id // 用唯一ID判断条目是否相同
            }

            override fun areContentsTheSame(oldItem: ListStoryItem, newItem: ListStoryItem): Boolean {
                return oldItem == newItem // 内容完全相等时返回true
            }
        }
    }
}

额外注意事项

  • 更新数据时,使用adapter.submitData(lifecycle, pagingData)提交分页数据,而非自定义方法
  • 确保ListStoryItem是Kotlin数据类(自动实现equals和hashCode),否则areContentsTheSame判断会出错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 19:18:09