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

Android SQLite更新旧记录Blob后无法读取的问题求助

解决SQLite更新大Blob后无法读取记录的问题

看起来你遇到的问题很典型——当更新了大尺寸的Blob(10MB)后,查询包含该Blob的记录时触发了CursorWindow的容量限制错误。我来帮你拆解问题并给出解决方案:

问题根源分析

你看到的IllegalStateException: Couldn't read row 0, col 0 from CursorWindow,核心原因是你的Blob大小(10MB)超过了Android系统中CursorWindow的默认容量上限(通常在2MB左右,不同版本略有差异)。

  • findAll()方法没问题,因为它只查询了小尺寸的字段(ID、名称、日期),这些数据能轻松放进CursorWindow;
  • get()方法要读取Blob字段,当Blob过大时,CursorWindow无法容纳整个Blob数据,直接导致Cursor读取失败,看起来像是返回了0行,但实际是读取时触发了错误。

另外还有一个潜在的小问题:你的get()方法里查询条件用了硬编码的"id = ?",而更新时用的是常量DatabaseHelper.BILL_ID_COLUMN + "=?",如果这个常量定义的不是"id",会导致查询条件不匹配,不过结合你更新前能正常读取的情况,这个应该不是当前的主因,但建议统一用常量避免后续问题。

解决方案

方案1:推荐——将图片存储到文件系统,数据库仅存路径

这是Android处理大文件的标准实践,SQLite并不适合存储大Blob,会带来性能和稳定性问题:

  • 保存图片时,将图片文件写入应用的私有目录(比如getFilesDir()),生成唯一文件名;
  • 数据库中只存储该文件的路径字符串;
  • 读取时根据路径从文件系统加载图片。

方案2:如果必须存数据库,改用流式读取Blob

如果一定要把Blob存在SQLite里,不要用cursor.getBlob()直接加载整个Blob,而是用SQLiteDatabase.openBlob()获取输入流,分段读取:

修改你的get()方法:

public Bill get(long id) {
    Bill bill = null;
    // 先查询除Blob外的字段
    Cursor cursor = database.query(DatabaseHelper.BILL_TABLE,
            new String[]{DatabaseHelper.BILL_ID_COLUMN, DatabaseHelper.BILL_NAME_COLUMN, DatabaseHelper.BILL_DUE_DATE_COLUMN},
            DatabaseHelper.BILL_ID_COLUMN + "=?", // 改用常量避免硬编码错误
            new String[]{String.valueOf(id)},
            null, null, null);
    
    if (cursor.moveToFirst()) {
        bill = new Bill();
        bill.setPersistent(true);
        bill.setId(cursor.getLong(cursor.getColumnIndex(DatabaseHelper.BILL_ID_COLUMN)));
        bill.setName(cursor.getString(cursor.getColumnIndex(DatabaseHelper.BILL_NAME_COLUMN)));
        bill.setDueDate(getDate(cursor.getString(cursor.getColumnIndex(DatabaseHelper.BILL_DUE_DATE_COLUMN))));
        
        // 单独读取Blob
        try (InputStream inputStream = database.openBlob(DatabaseHelper.BILL_TABLE,
                DatabaseHelper.BILL_IMAGE_COLUMN,
                DatabaseHelper.BILL_ID_COLUMN + "=?",
                new String[]{String.valueOf(id)},
                null)) {
            // 将InputStream转为byte[]
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }
            bill.setImage(baos.toByteArray());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    cursor.close();
    return bill;
}

额外注意事项

  • 确保你的DatabaseHelper中定义的表结构里,Blob字段的类型是BLOB;
  • 更新Blob时,确保传入的byte[]没有损坏,可以在更新前后打印字节数组的长度来验证;
  • 定期优化数据库:调用database.execSQL("VACUUM;"),避免Blob更新导致的数据库碎片问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:57:40