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

Activity重建后填充RecyclerView静态列表触发数组越界崩溃排查

问题描述

应用在Activity重建后频繁崩溃,崩溃堆栈如下:

Choreographer.java line 911

Fatal Exception: java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0
   at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
   at java.util.ArrayList.get(ArrayList.java:308)
   at android.animation.ObjectAnimator.start(ObjectAnimator.java:817)
   at android.animation.AnimatorSet$DependencyListener.startIfReady(AnimatorSet.java:799)
   at android.animation.AnimatorSet$DependencyListener.onAnimationEnd(AnimatorSet.java:753)
   at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1239)
   at android.animation.ValueAnimator.cancel(ValueAnimator.java:1140)
   at android.animation.ObjectAnimator.start(ObjectAnimator.java:820)
   at android.animation.AnimatorSet$DependencyListener.startIfReady(AnimatorSet.java:799)
   at android.animation.AnimatorSet$DependencyListener.onAnimationEnd(AnimatorSet.java:753)
   at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1239)
   at android.animation.ValueAnimator.cancel(ValueAnimator.java:1140)
   at android.animation.ObjectAnimator.animateValue(ObjectAnimator.java:974)
   at android.animation.ValueAnimator.animationFrame(ValueAnimator.java:1384)
   at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1427)
   at android.animation.ValueAnimator$AnimationHandler.doAnimationFrame(ValueAnimator.java:759)
   at android.animation.ValueAnimator$AnimationHandler$1.run(ValueAnimator.java:801)
   at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
   at android.view.Choreographer.doCallbacks(Choreographer.java:686)
   at android.view.Choreographer.doFrame(Choreographer.java:619)
   at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
   at android.os.Handler.handleCallback(Handler.java:739)
   at android.os.Handler.dispatchMessage(Handler.java:95)
   at android.os.Looper.loop(Looper.java:148)
   at android.app.ActivityThread.main(ActivityThread.java:7409)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

RecyclerView初始化代码:

private fun initRecyclerView() {
    val list = ArrayList<HomeFeature>()
    list.add(HomeFeature(id = 0, iconId = R.drawable.ic_home_collage, imageId = R.drawable.ic_home_bg_collage, title = globalActivity.resources.getString(R.string.home_title_1)))
    list.add(HomeFeature(id = 2, iconId = R.drawable.ic_home_edit, imageId = R.drawable.ic_home_bg_camera, title = globalActivity.resources.getString(R.string.home_title_3)))
    list.add(HomeFeature(id = 1, iconId = R.drawable.ic_home_camera, imageId = R.drawable.ic_home_bg_edit, title = globalActivity.resources.getString(R.string.home_title_2)))
    list.add(HomeFeature(id = 3, iconId = R.drawable.ic_home_filters, imageId = R.drawable.ic_home_bg_filters, title = globalActivity.resources.getString(R.string.home_title_4)))
    list.add(HomeFeature(id = 5, iconId = R.drawable.ic_home_settings, imageId = R.drawable.ic_home_bg_settings, title = globalActivity.resources.getString(R.string.home_title_6)))
    list.add(HomeFeature(id = 4, iconId = R.drawable.ic_home_saved, imageId = R.drawable.ic_home_bg_saved, title = globalActivity.resources.getString(R.string.home_title_5)))
    binding.incItemCollageHome.item = list[0]
    binding.incItemEditHome.item = list[1]
    binding.incItemCollageHome.itemClick = itemClick
    binding.incItemEditHome.itemClick = itemClick

    val adapter = AdapterHome(itemClick)
    binding.rvFeaturesHome.adapter = adapter
    val subList = list.subList(2, 6)
    adapter.submitList(subList)
}

Adapter实现代码:

class AdapterHome(private val onItemClickListener: OnItemClickListener) : ListAdapter<HomeFeature, AdapterHome.ViewHolderHome>(diffUtilHome) {

    companion object {
        val diffUtilHome = object : DiffUtil.ItemCallback<HomeFeature>() {
            override fun areItemsTheSame(oldItem: HomeFeature, newItem: HomeFeature): Boolean {
                return oldItem.id == newItem.id
            }

            override fun areContentsTheSame(oldItem: HomeFeature, newItem: HomeFeature): Boolean {
                return oldItem == newItem
            }
        }
    }

    inner class ViewHolderHome(val binding: ListItemHomeBinding) : RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderHome {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = DataBindingUtil.inflate<ListItemHomeBinding>(layoutInflater, R.layout.list_item_home, parent, false)
        return ViewHolderHome(binding)
    }

    override fun onBindViewHolder(holder: ViewHolderHome, position: Int) {
        val homeFeature = getItem(position)
        holder.binding.apply {
            item = homeFeature
            itemClick = onItemClickListener
        }
    }
}
崩溃原因分析
  1. 核心触发点:崩溃来自ObjectAnimator尝试访问一个size为0的ArrayList的索引0,属于数组越界异常。
  2. 关联场景:仅在Activity重建后出现,结合代码逻辑,主要有两个可能:
    • SubList生命周期问题:代码中使用list.subList(2,6)作为Adapter数据源,而subList是原ArrayList的视图(非独立副本),依赖原list的引用。当原list作为局部变量在方法执行完毕后,若因内存回收或Activity重建导致原list失效,subList的内部状态会异常,size变为0,动画执行时触发越界。
    • 残留动画干扰:Activity重建前,RecyclerView的默认ItemAnimator可能仍在执行动画,重建后旧的动画回调未被清理,此时旧数据源已销毁,动画回调访问空列表导致崩溃。
解决方法

方法1:将SubList转为独立列表副本

切断SubList与原局部list的依赖,创建独立的ArrayList实例:

// 替换原subList代码
val subList = ArrayList(list.subList(2, 6))
adapter.submitList(subList)

方法2:清理Activity重建时的残留动画

在onDestroyView中主动停止RecyclerView的所有动画,解除旧Adapter关联:

override fun onDestroyView() {
    super.onDestroyView()
    binding.rvFeaturesHome.itemAnimator?.run {
        endAnimations()
        isRunning = false
    }
    binding.rvFeaturesHome.adapter = null
}

方法3:复用Adapter避免重复创建

将Adapter作为Fragment/Activity的成员变量,重建时复用实例,仅更新数据:

// 定义成员变量
private lateinit var adapter: AdapterHome

private fun initRecyclerView() {
    // ... 初始化list的代码 ...
    if (!::adapter.isInitialized) {
        adapter = AdapterHome(itemClick)
        binding.rvFeaturesHome.adapter = adapter
    }
    val subList = ArrayList(list.subList(2, 6))
    adapter.submitList(subList)
}

方法4:检查自定义视图动画

检查头部视图incItemCollageHome、incItemEditHome的布局或绑定逻辑,确认是否存在ObjectAnimator持有旧数据引用的情况,重建时重置相关动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:20:27