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

Bitmap数组存入SharedPreferences触发OutOfMemoryError,求解决方案

解决SharedPreferences存储Bitmap数组导致OutOfMemoryError的问题

兄弟你这是踩了个典型的坑——直接把Bitmap对象序列化到SharedPreferences里,这绝对会触发内存爆炸!咱们先拆解清楚问题出在哪,再给你两个靠谱的解决方案。

问题根源分析

你现在的做法是把每个Drawable解码成Bitmap,再用Gson把整个Bitmap数组转成JSON字符串,这里有两个致命问题:

  1. Bitmap本身内存开销大:哪怕是mdpi的图片,解码成ARGB_8888格式的Bitmap后,每个像素要占4字节,26张图加起来内存就已经不小了。
  2. Gson序列化Bitmap会生成巨量文本:Gson会把Bitmap的所有像素数据转成纯文本格式,这会让体积膨胀好几倍——报错里提到要分配300MB内存,就是这个原因,直接超出了你的可用内存上限。

而且SharedPreferences从设计上就不适合存储大体积数据,官方建议单条数据不要超过100KB,你直接存几百MB的字符串,完全违背了它的使用场景。

解决方案:两种可行思路

思路1:压缩Bitmap为Base64字符串(适合小图/少量图)

如果你的图片本身尺寸不大,可以先把Bitmap压缩成JPEG/PNG字节数组,再转成Base64字符串,这样体积会大幅缩小,再存入SP。

修改你的代码如下:

ArrayList<Model> Combinebitmaps = new ArrayList<>();
int arr[] = {R.drawable.b1, R.drawable.b2, ..., R.drawable.b26};

for (int i = 0; i < arr.length; i++) {
    // 解码时先压缩,减少内存占用
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 2; // 宽高缩为原来的1/2,内存占用降到原来的1/4
    Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), arr[i], options);
    
    // 把Bitmap压缩为JPEG字节数组,质量设为50(可根据需求调整)
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 50, baos);
    byte[] bitmapBytes = baos.toByteArray();
    // 转成Base64字符串
    String bitmapBase64 = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
    
    Model model = new Model(bitmapBase64); // 注意修改Model类,存储String类型的Base64
    Combinebitmaps.add(model);
    
    // 及时回收Bitmap,释放内存
    if (!bitmap.isRecycled()) {
        bitmap.recycle();
    }
}

// 序列化Base64数组到SP,用apply()异步提交更安全
SharedPreferences sharedPreferences = MainActivity.this.getSharedPreferences("ArrayCombine", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("Array", new Gson().toJson(Combinebitmaps));
editor.apply();

注意:你需要修改Model类,把原来存储Bitmap的字段改成存储String(Base64)。读取时再把Base64转回Bitmap即可。

思路2:将Bitmap保存为文件,SP存储文件路径(适合多图/大图)

这是更推荐的方案,因为SP根本不是用来存大量二进制数据的。我们把每个Bitmap保存到应用内部存储,然后在SP里存储这些文件的路径列表,这样SP里只存少量字符串,完全不会有内存压力。

代码示例:

ArrayList<String> bitmapPaths = new ArrayList<>();
int arr[] = {R.drawable.b1, R.drawable.b2, ..., R.drawable.b26};

for (int i = 0; i < arr.length; i++) {
    Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), arr[i]);
    
    // 创建存储文件的路径,比如内部存储的files目录下
    File file = new File(getFilesDir(), "bitmap_" + i + ".jpg");
    try (FileOutputStream fos = new FileOutputStream(file)) {
        // 压缩Bitmap并写入文件,质量设为80兼顾清晰度和体积
        bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
        bitmapPaths.add(file.getAbsolutePath()); // 保存文件路径
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    // 回收Bitmap
    if (!bitmap.isRecycled()) {
        bitmap.recycle();
    }
}

// 把路径列表序列化到SP
SharedPreferences sharedPreferences = MainActivity.this.getSharedPreferences("ArrayCombine", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("Array", new Gson().toJson(bitmapPaths));
editor.apply();

读取的时候,从SP取出路径列表,再根据路径解码Bitmap:

SharedPreferences sharedPreferences = getSharedPreferences("ArrayCombine", Context.MODE_PRIVATE);
String pathsJson = sharedPreferences.getString("Array", "");
ArrayList<String> bitmapPaths = new Gson().fromJson(pathsJson, new TypeToken<ArrayList<String>>(){}.getType());

ArrayList<Bitmap> bitmaps = new ArrayList<>();
for (String path : bitmapPaths) {
    Bitmap bitmap = BitmapFactory.decodeFile(path);
    bitmaps.add(bitmap);
}

额外注意事项

  • 解码Bitmap时的优化:始终用BitmapFactory.Options设置采样率(inSampleSize),或者设置inPreferredConfig为RGB_565(每个像素占2字节,比ARGB_8888省一半内存)。
  • 及时回收Bitmap:不再使用的Bitmap一定要调用recycle(),并置为null,帮助GC快速回收内存。
  • 别依赖largeHeap:android:largeHeap="true"只是临时缓解手段,会让应用占用更多系统资源,不是根本解决方案,尽量不要依赖它。

内容的提问来源于stack exchange,提问作者M Dilawar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:52:01