Kotlin报错CursorWindow读取失败,无法加载Fragment求助
这个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

