使用Glide+RecyclerView加载图片,300张后出现内存溢出问题求助
嘿,我来帮你搞定这个内存溢出的问题!虽然单张图片只有4KB,但你得知道:图片在内存中的占用是按像素计算的,不是原文件大小——哪怕原文件很小,加载成Bitmap后可能会占用好几倍甚至几十倍的内存,300张叠加起来就很容易爆内存了。结合你的代码和场景,我给你几个针对性的优化方案:
GridLayout里的ImageView尺寸是固定的,但Glide可能没有正确识别控件的实际大小,导致加载了远超需求的Bitmap。你可以直接指定加载的尺寸,让Glide只生成适合控件的Bitmap:
@Override public void onBindViewHolder(ViewHolder holder, int position) { // 先清除复用View上的旧请求,避免资源浪费 Glide.with(mMainActivity).clear(holder.posterPath); MovieItem item = mList.get(position); Glide.with(mMainActivity) .load(item.getPosterPath()) .thumbnail(0.5f) // 直接指定ImageView的实际宽高,或者写死固定的像素值(比如你的Grid item是100x150) .override(holder.posterPath.getWidth(), holder.posterPath.getHeight()) .centerCrop() // 或者fitCenter,根据你的布局需求选择 .into(holder.posterPath); }
如果你的Grid item尺寸是固定的(比如xml里用dp指定了宽高),直接写死像素值会更稳妥,比如override(120, 180)(换成你的实际尺寸)。
默认的LRU缓存可能不够适配你的场景,你可以手动调整缓存大小,同时开启Bitmap复用减少内存碎片:
先创建一个GlideModule类:
@GlideModule public class CustomGlideModule extends AppGlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { // 设置内存缓存大小,比如取设备可用内存的1/8,或者直接设20MB(根据设备调整) int memoryCacheSize = 1024 * 1024 * 20; builder.setMemoryCache(new LruResourceCache(memoryCacheSize)); // 开启Bitmap复用池,减少内存分配和碎片 builder.setBitmapPool(new LruBitmapPool(memoryCacheSize)); } }
然后在AndroidManifest.xml里添加这个Module的配置:
<meta-data android:name="com.yourpackage.CustomGlideModule" android:value="GlideModule" />
一定要在绑定新数据前清除旧的Glide请求,避免复用的ImageView加载旧图片,同时释放不必要的资源:
@Override public void onBindViewHolder(ViewHolder holder, int position) { // 关键:清除之前的加载请求,防止内存泄漏和旧图片残留 Glide.with(mMainActivity).clear(holder.posterPath); // 然后加载新图片 MovieItem item = mList.get(position); Glide.with(mMainActivity) .load(item.getPosterPath()) .thumbnail(0.5f) .override(holder.posterPath.getWidth(), holder.posterPath.getHeight()) .into(holder.posterPath); }
另外,别在Adapter外部持有ViewHolder的强引用,避免内存泄漏。
如果你的图片不需要透明通道,可以把解码格式换成RGB_565,这样每个像素的内存占用从4字节降到2字节,直接减少一半内存:
Glide.with(mMainActivity) .load(item.getPosterPath()) .thumbnail(0.5f) .format(DecodeFormat.PREFER_RGB_565) .override(holder.posterPath.getWidth(), holder.posterPath.getHeight()) .into(holder.posterPath);
如果这些图片不需要快速复用,可以跳过内存缓存,只缓存到磁盘,进一步减少内存占用:
Glide.with(mMainActivity) .load(item.getPosterPath()) .thumbnail(0.5f) .skipMemoryCache(true) // 跳过内存缓存 .diskCacheStrategy(DiskCacheStrategy.RESULT) // 只缓存处理后的图片,节省磁盘空间 .into(holder.posterPath);
不过这个要根据业务需求来,如果需要快速滑动加载,还是保留内存缓存更合适。
最后,你可以用Android Profiler的Memory面板查看内存占用详情,确认是不是Bitmap占用了大部分内存,也可以排查有没有其他内存泄漏的情况(比如Activity的引用被意外持有)。
内容的提问来源于stack exchange,提问作者Overflow 404

