Android存储含Bitmap的自定义ArrayList时出现无效Bitmap报错的解决咨询
崩溃根因
- 你遇到的崩溃本质是Gson直接序列化Bitmap系统原生对象导致的安全校验触发:Gson默认会通过反射遍历类的所有非静态、非transient字段,Bitmap类内部持有指向系统原生图形内存的指针、回收状态标记,这些字段是禁止应用层随意访问的,一旦被Gson反射读取,就会触发
A/Bitmap: Error, cannot access an invalid/free'd bitmap here!的校验错误,直接走Runtime abort流程打印堆栈后终止程序。 - SharedPreferences的设计定位是存储小体积配置键值,本身不适合承载大体积的Bitmap类数据,就算绕开了序列化问题,把全分辨率Bitmap转成字符串存进SP也会导致SP文件臃肿、读写卡顿、ANR甚至数据丢失。
- 反序列化阶段Gson没有原生支持Bitmap的重建逻辑,就算序列化过程不崩,反序列化时也会读到无效的内存地址,依然会触发Bitmap访问崩溃。
可落地修复方案
1. 调整数据模型,拆分可序列化字段和运行时对象
修改ImageModel类,不把Bitmap作为可序列化字段,改用本地文件路径存储图片的持久化位置,Bitmap标记为运行时临时加载的对象:
public class ImageModel { // 持久化只存图片的本地文件绝对路径,属于String类型可直接被Gson序列化 private String localImagePath; // 用transient修饰Bitmap字段,Gson序列化时会自动跳过该字段,不会触发读取Bitmap内部状态的逻辑 private transient Bitmap mBitmap; // 运行时按需加载Bitmap的方法 public Bitmap getBitmap() { // 内存中没有缓存才从本地文件读取,避免重复解码占内存 if (mBitmap == null && localImagePath != null) { mBitmap = BitmapFactory.decodeFile(localImagePath); } return mBitmap; } }
原有的Doc类结构不需要修改,其中的name、date、ArrayList<ImageModel>都是Gson可正常序列化的结构。
2. 新增Bitmap本地落盘逻辑
不要把Bitmap直接塞入序列化流程,在保存数据前,把每个ImageModel里的Bitmap写到应用私有目录(无需申请存储权限),拿到路径后再做序列化:
private String saveBitmapToPrivateDir(Bitmap bitmap) { // 应用专属内部存储目录,外部应用无法访问,不需要动态申请权限 File imageDir = new File(getExternalFilesDir(null), "doc_images"); if (!imageDir.exists()) { imageDir.mkdirs(); } // 用UUID生成唯一文件名,避免不同文档的图片互相覆盖 File targetFile = new File(imageDir, UUID.randomUUID() + ".jpg"); FileOutputStream fos = null; try { fos = new FileOutputStream(targetFile); // 按90%质量压缩存JPG,减少文件体积 bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos); fos.flush(); return targetFile.getAbsolutePath(); } catch (Exception e) { e.printStackTrace(); return null; } finally { // 实际开发记得补全流关闭逻辑 } }
3. 调整原有saveData、loadData的执行流程
- 保存流程:遍历
DocList里的所有ImageModel,如果对应的Bitmap还没有绑定本地文件路径,就调用上面的落盘方法把Bitmap存到本地,把返回的路径赋值给localImagePath字段,之后再走原有的Gson序列化、存SP的逻辑。因为Bitmap字段加了transient修饰,Gson完全不会触碰Bitmap对象,也就不会触发崩溃。 - 加载流程:直接用原有的Gson反序列化逻辑从SP读出带本地图片路径的
DocList即可,后续页面需要展示图片时,调用ImageModel.getBitmap()方法按需解码本地文件拿到Bitmap对象,不需要依赖Gson重建Bitmap。
避坑提醒
- 不要尝试给Gson写自定义的Bitmap适配器,把Bitmap全量像素转成Base64字符串存进SP:这种方式会生成几MB甚至几十MB的超长字符串,超出SP的设计容量,读写时会造成主线程长时间阻塞,甚至触发系统的SP单键值大小限制导致数据丢失。
- 如果单条文档的图片数量多、分辨率高,不要把文档元数据存在SP里,可换用Room做结构化存储,SP仅保留轻量的配置项即可。
- 页面销毁时记得对加载的Bitmap做内存回收,避免内存溢出。
内容的提问来源于stack exchange,提问作者Tanatorn Boonprasert
相关产品推荐
相关产品推荐

