从ActiveAndroid数据库获取ImageList时触发OutOfMemory Error
看起来你已经搞定了拍照后的OOM问题,但在从ActiveAndroid加载未上传图片列表时又栽了跟头——而且这个OOM是在Cursor解析字符串的时候触发的,从错误日志看,系统尝试分配422KB内存但只剩335KB可用,这说明你的ImageUploadModel里存储的图片相关数据(大概率是Base64格式的图片内容或者完整的二进制数据)太大,一次性加载多条记录时,CursorWindow要把这些大字符串全部加载到内存里,直接撑爆了。
给你几个针对性的解决方案,按优先级排序:
1. 改用文件路径存储(最推荐)
不要再把完整的图片数据存在数据库里,而是将图片保存到内部存储后,把文件的绝对路径存在ImageUploadModel中。这样数据库里只存短字符串路径,加载列表时内存占用可以忽略不计,需要上传或显示图片时,再通过路径去读取文件。
代码调整示例:
修改ImageUploadModel的字段:
public class ImageUploadModel extends Model { // 替换原来的imageData(Base64或二进制字段) public String imagePath; // 其他字段... public String intimationId; public boolean isImageDetailsUpload; }
查询时就不会加载大体积数据,自然不会OOM:
public static List<ImageUploadModel> getUnUploadedImageListonintimation(String IntimationId) { return new Select() .from(ImageUploadModel.class) .where("intimationId=?", IntimationId) .where("isImageDetailsUpload=?", false) .execute(); }
2. 分批分页加载数据
如果暂时无法修改存储方式,那就不要一次性加载所有未上传图片,改用分页查询,每次只加载少量记录(比如10条),处理完这批再加载下一批。
代码调整示例:
修改查询方法支持分页:
public static List<ImageUploadModel> getUnUploadedImageListonintimation(String IntimationId, int offset, int limit) { return new Select() .from(ImageUploadModel.class) .where("intimationId=?", IntimationId) .where("isImageDetailsUpload=?", false) .offset(offset) .limit(limit) .execute(); }
在后台服务中循环加载:
int limit = 10; int offset = 0; List<ImageUploadModel> batch; do { batch = ImageUploadModel.getUnUploadedImageListonintimation(uploadImageModel.getIntimationId(), offset, limit); // 处理当前批次的图片 processImages(batch); offset += limit; } while (!batch.isEmpty());
3. 延迟加载大字段
如果必须在数据库中保留图片数据,可以在列表加载时只查询必要的元数据(比如ID、intimationId、上传状态),不加载图片字段。等到需要使用图片数据时,再单独查询该条记录的图片字段。
代码调整示例:
public static List<ImageUploadModel> getUnUploadedImageMetadata(String IntimationId) { List<ImageUploadModel> list = new ArrayList<>(); Cursor cursor = null; try { // 只查询需要的字段,排除大体积的imageData String query = "SELECT id, intimationId, isImageDetailsUpload FROM ImageUploadModel WHERE intimationId=? AND isImageDetailsUpload=?"; cursor = SQLiteDatabase.openDatabase(ActiveAndroid.getDatabase().getPath(), null, SQLiteDatabase.OPEN_READONLY) .rawQuery(query, new String[]{IntimationId, "0"}); while (cursor.moveToNext()) { ImageUploadModel model = new ImageUploadModel(); model.setId(cursor.getLong(cursor.getColumnIndex("id"))); model.setIntimationId(cursor.getString(cursor.getColumnIndex("intimationId"))); model.setIsImageDetailsUpload(cursor.getInt(cursor.getColumnIndex("isImageDetailsUpload")) == 1); list.add(model); } } finally { if (cursor != null) { cursor.close(); } } return list; } // 当需要图片数据时,单独查询 public static ImageUploadModel getImageModelWithData(long modelId) { return new Select().from(ImageUploadModel.class).where("id=?", modelId).executeSingle(); }
4. 再次优化图片压缩
检查你当前的图片压缩逻辑,确保压缩后的图片尺寸和质量已经足够小——比如上传用的图片不需要和原始拍照尺寸一样大,可以压缩到1080p以内,质量调到70%左右,进一步减少单条记录的数据体积。
内容的提问来源于stack exchange,提问作者Shashwat Gupta

