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

Android图片展示App添加图片卡顿及OOM问题求助

解决图片展示App的内存溢出与加载缓慢问题

嘿,你的问题根源其实是**对图片的处理方式太“笨重”**了——直接加载原图、转Base64存SharedPreferences,这两个操作几乎是OOM和卡顿的元凶。咱们一步步来优化:

核心问题分析

  • 内存爆炸的元凶:直接解码原图为Bitmap,再转成Base64存储。一张1080P的PNG图可能占几十MB内存,多张叠加直接触发OOM;而且Base64会让数据体积膨胀33%,SharedPreferences根本不适合存大量二进制数据。
  • 加载缓慢的原因:每次添加图片都调用notifyDataSetChanged()刷新整个RecyclerView;Adapter中每次绑定都重复解码Base64为Bitmap,没有任何缓存机制。

具体优化方案

1. 放弃Base64,改用存储图片Uri

我们不需要把图片转成Base64存起来,直接保存图片的Uri字符串就够了——Uri本身只是个地址,体积极小,加载时再通过Uri去获取图片。

首先修改你的ImageModelClass,把image字段改为存储Uri字符串:

public class ImageModelClass {
    private String imageUri; // 替换原来的Base64字段

    public String getImageUri() {
        return imageUri;
    }

    public void setImageUri(String imageUri) {
        this.imageUri = imageUri;
    }
}

2. 修改Activity的图片选择与存储逻辑

在onActivityResult中,不再解码Bitmap,直接保存Uri的字符串:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
        assert data != null;
        if (data.getClipData() != null) {
            ClipData clipData = data.getClipData();
            for (int i = 0; i < clipData.getItemCount(); i++) {
                Uri imageUri = clipData.getItemAt(i).getUri();
                ImageModelClass model = new ImageModelClass();
                model.setImageUri(imageUri.toString());
                imageList.add(model);
                // 只刷新新增的item,比notifyDataSetChanged高效N倍
                adapter.notifyItemInserted(imageList.size() - 1);
            }
        } else {
            Uri imageUri = data.getData();
            if (imageUri != null) {
                ImageModelClass model = new ImageModelClass();
                model.setImageUri(imageUri.toString());
                imageList.add(model);
                adapter.notifyItemInserted(imageList.size() - 1);
            }
        }
        saveGalleryData();
    }
}

同时更新存储和加载方法(逻辑不变,但存储的是Uri列表,体积更小):

private void saveGalleryData() {
    SharedPreferences preferences = getSharedPreferences(IMAGE_SHARED_PREFS, MODE_PRIVATE);
    SharedPreferences.Editor editor = preferences.edit();
    Gson gson = new Gson();
    String json = gson.toJson(imageList);
    editor.putString(IMAGE_DATA_KEY, json);
    editor.apply();
}

private void loadAlbumData() {
    SharedPreferences preferences = getSharedPreferences(IMAGE_SHARED_PREFS, MODE_PRIVATE);
    Gson gson = new Gson();
    Type type = new TypeToken<ArrayList<ImageModelClass>>() {}.getType();
    String data = preferences.getString(IMAGE_DATA_KEY, null);
    imageList = gson.fromJson(data, type);
    if (imageList == null) {
        imageList = new ArrayList<>();
    }
}

3. 用Glide处理图片加载(彻底解决OOM)

Glide是Android上最常用的图片加载库,它会自动:

  • 根据ImageView的尺寸采样压缩图片,只加载需要的分辨率
  • 管理内存和磁盘缓存,避免重复加载
  • 自动回收Bitmap,防止内存泄漏

首先在build.gradle中添加Glide依赖(记得同步项目):

dependencies {
    implementation 'com.github.bumptech.glide:glide:4.16.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
}

然后修改Adapter,用Glide加载图片,去掉手动解码Base64的逻辑:

public class ImageCollectionAdapter extends RecyclerView.Adapter<ImageCollectionAdapter.MyViewHolder> {
    private List<ImageModelClass> list;
    private Context context; // 添加Context用于Glide初始化

    public ImageCollectionAdapter(List<ImageModelClass> list, Context context) {
        this.list = list;
        this.context = context;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MyViewHolder(LayoutInflater.from(parent.getContext())
                .inflate(R.layout.image_collection_lists, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        ImageModelClass currentImage = list.get(position);
        Uri imageUri = Uri.parse(currentImage.getImageUri());
        
        // 用Glide加载图片,自动处理压缩和缓存
        Glide.with(context)
                .load(imageUri)
                .centerCrop() // 可选,根据你的布局需求调整为fitCenter等
                .into(holder.imageView);
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        RoundedImageView imageView;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.galleryPicture);
        }
    }
}

最后在Activity中初始化Adapter时传入Context:

adapter = new ImageCollectionAdapter(imageList, this);

4. 额外优化:清理无用代码

删除Activity中不再需要的encodeTobase64方法,以及Adapter中相关的Base64解码逻辑——这些现在都是多余的了。


优化后的效果

  • 内存占用大幅降低:Glide只会加载ImageView尺寸对应的小Bitmap,不会一次性加载多张原图
  • 添加图片速度明显提升:不再需要解码和转Base64,只存Uri字符串
  • 滚动更流畅:RecyclerView只刷新新增item,Glide的缓存避免重复加载

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 12:52:34