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

Android使用Retrofit2填充ArrayList时出现IndexOutOfBoundsException异常

问题根本原因

  1. 异步调用时序问题:你使用retrofitData.enqueue()发起的是异步网络请求,代码不会等待请求返回再往下执行。FetchContent的init代码块中调用完getRetroFitData()后立刻循环调用createFetchItem()读取数据,此时网络请求还未完成,retroId等存储数据的数组完全是空的,自然会抛出下标越界异常。你调试时看到的数据是请求返回后才存入的,和崩溃发生的时机不重合。
  2. 实例不共享问题:你每次调用fetchedData()都是新建一个类实例,请求成功后数据存在了临时实例的私有变量中,后续调用fetchedData().getID()拿到的是全新的空实例,之前存储的数据完全无法访问。

修复方案

第一步:调整数据存储类为单例,增加加载回调

将fetchedData改为单例保证全局共享同一个实例,同时增加数据加载完成的回调,解决异步时序问题:

// 类名调整为大驼峰,符合Kotlin命名规范
object FetchedData {
    private val retroId = arrayListOf<String>()
    private val retroListId = arrayListOf<String>()
    private val retroName = arrayListOf<String>()

    fun getRetroFitData(onLoaded: ()->Unit) {
        val retrofitBuilder = Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl(PRIMARY_URL)
            .build()
            .create(interfaceAPI::class.java)

        val retrofitData = retrofitBuilder.getData()

        retrofitData.enqueue(object : Callback<List<FetchContent.FetchDataItem>?> {
            override fun onResponse(
                call: Call<List<FetchContent.FetchDataItem>?>,
                response: Response<List<FetchContent.FetchDataItem>?>
            ) {
                val responseBody = response.body() ?: return
                // 先清空旧数据避免重复添加
                retroId.clear()
                retroListId.clear()
                retroName.clear()
                for (item in responseBody) {
                    if (!item.name.isNullOrEmpty()) {
                        retroId.add(item.id)
                        retroListId.add(item.listId)
                        retroName.add(item.name)
                    }
                }
                // 数据加载完成后触发回调
                onLoaded.invoke()
            }

            override fun onFailure(call: Call<List<FetchContent.FetchDataItem>?>, t: Throwable) {
                Log.d("FetchContent", "onFailure:"+t.message)
            }
        })
    }

    // getter方法增加下标越界判断
    fun getID(index: Int): String {
        return if (index >= retroId.size || retroId[index].isEmpty())
            "null"
        else
            retroId[index]
    }

    fun getListID(index: Int): String {
        return if (index >= retroListId.size) "null" else retroListId[index]
    }

    fun getName(index: Int): String {
        return if (index >= retroName.size) "null" else retroName[index]
    }

    // 新增获取数据总条数的方法
    fun getDataSize(): Int = retroId.size
}

第二步:修改FetchContent逻辑,移除init块的硬编码循环

不要在init块中硬写固定长度的循环,等数据加载完成后再填充列表:

object FetchContent {
    val jsonITEMS: MutableList<FetchDataItem> = ArrayList()

    fun loadData(onLoaded: ()->Unit) {
        FetchedData.getRetroFitData {
            jsonITEMS.clear()
            // 根据实际返回的数据条数生成列表项
            for (i in 0 until FetchedData.getDataSize()) {
                jsonITEMS.add(createFetchItem(i))
            }
            onLoaded.invoke()
        }
    }

    private fun createFetchItem(position: Int): FetchDataItem {
        return FetchDataItem(
            FetchedData.getID(position), FetchedData.getListID(position), FetchedData.getName(position)
        )
    }

    data class FetchDataItem(val id: String, val listId: String, val name: String) {
        override fun toString(): String = listId
    }
}

第三步:修改ItemFragment,异步加载完成后更新适配器

先给RecyclerView设置空适配器,等数据加载完成后再更新数据:

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val view = inflater.inflate(R.layout.fragment_item_list, container, false)

    if (view is RecyclerView) {
        with(view) {
            layoutManager = when {
                columnCount <= 1 -> LinearLayoutManager(context)
                else -> GridLayoutManager(context, columnCount)
            }
            // 先设置空适配器
            adapter = MyItemRecyclerViewAdapter(emptyList())
            // 异步加载数据
            FetchContent.loadData {
                // 切回主线程更新UI
                activity?.runOnUiThread {
                    adapter = MyItemRecyclerViewAdapter(FetchContent.jsonITEMS)
                    // 也可以给适配器新增updateData方法更新数据,避免重复创建实例,性能更优
                }
            }
        }
    }
    return view
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 16:45:02