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
相关产品推荐
相关产品推荐

