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

Kotlin报错CursorWindow读取失败,无法加载Fragment求助

排查并解决Cursor列索引-1的报错问题

这个java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow错误的核心原因非常明确:你尝试通过cursor.getColumnIndex()获取的列名在当前Cursor中不存在,导致返回了无效的索引-1。下面我们一步步排查并解决问题:

可能的原因与针对性修复

1. 验证Cursor实际包含的列名

首先要确认数据库返回的Cursor到底有哪些列,这是快速定位问题的关键。在你的fetchItems()方法中添加日志,打印所有可用列名:

fun Context.fetchItems() : MutableList<MyItem> {
    val items = mutableListOf<MyItem>()
    val cursor = contentResolver.query(MY_PROVIDER_CONTENT_URI,
            null, null, null, null)
    
    cursor?.let {
        // 打印所有列名
        val columnNames = it.columnNames.joinToString(", ")
        Log.d("CursorDebug", "Available columns: $columnNames")
        
        // 逐个检查目标列的索引
        val idIndex = it.getColumnIndex(MyItem::id.name)
        val nameIndex = it.getColumnIndex(MyItem::name.name)
        val readIndex = it.getColumnIndex(MyItem::read.name)
        Log.d("CursorDebug", "idIndex: $idIndex, nameIndex: $nameIndex, readIndex: $readIndex")
        
        it.use { c ->
            while (c.moveToNext()) {
                // 暂时简化构造MyItem,先验证核心字段
                items.add(
                    MyItem(
                        c.getLong(idIndex),
                        c.getString(nameIndex),
                        "", "", "", "", "", "", "", "", "", 
                        c.getInt(readIndex) == 1
                    )
                )
            }
        }
    }
    return items
}

运行后查看日志,就能清楚看到哪个列的索引是-1,直接锁定问题字段。

2. 检查数据库表结构与属性名的一致性

你用MyItem::xx.name来定义数据库列名,这个写法本身很安全,但要确保两个关键点:

  • MyItem的每个属性名和你从API解析的CharacterItem属性名完全一致(比如origin、location的名字没有拼写差异)
  • 数据库表结构确实包含所有你要查询的列:
    比如你在CREATE_TABLE中定义了read列,但如果是后期新增的列,却没有升级数据库版本,旧的数据库文件不会自动更新结构,导致这个列不存在。

解决数据库版本问题:

  • 测试阶段可以直接卸载应用重装,让数据库重新初始化
  • 正式环境需要升级DB_VERSION并添加迁移逻辑:
private const val DB_VERSION = 2 // 从1改为2

override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
    when {
        oldVersion < 2 -> {
            // 新增read列,注意设默认值避免冲突
            db.execSQL("ALTER TABLE $TABLE_NAME ADD COLUMN ${MyItem::read.name} integer not null default 0")
        }
    }
}

3. 处理API中origin和location的对象类型

Rick and Morty API返回的origin和location是JSON对象(结构为{"name": "...", "url": "..."}),但你的CharacterItem中将它们定义为String?。如果你的解析逻辑没处理好,可能导致这两个字段没有正确存入数据库,或者存入的值不符合预期。

确保解析时将对象转换为字符串,比如用Gson的toJson()将对象序列化为字符串再存入:

// 假设你用Retrofit+Gson解析API数据
val character = response.body()
val originStr = Gson().toJson(character.origin)
val locationStr = Gson().toJson(character.location)
// 然后将originStr、locationStr存入数据库对应的列

4. 修复Cursor的资源泄漏问题

你的原代码没有关闭Cursor,这会导致内存泄漏,建议用Kotlin的use块自动管理Cursor生命周期:

cursor?.use { c ->
    while (c.moveToNext()) {
        // 读取数据并构造MyItem
    }
}

总结

这个问题的本质是列名不匹配或数据库结构未更新,通过打印Cursor的列名和索引可以快速定位到具体字段,然后针对性修复:要么修正属性名与列名的差异,要么升级数据库版本更新结构,同时确保API字段的解析逻辑正确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 09:55:15