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

Android RecyclerView图片加载内存优化:Glide与Fresco选型困境

问题概述

我开发了一款从API获取带图片数据的App,用RecyclerView展示图片,支持滚动加载更多。测试了Glide和Fresco两款图片加载库,目前Glide内存表现更好,但图片存在模糊问题;Fresco图片清晰但内存占用过高。相关代码和内存数据如下:


Glide 实现细节

图片加载代码

Glide.with(holder.gameImage)
            .load(itemViewModel.thumb)
            .skipMemoryCache(true)
            .apply(RequestOptions().override(holder.gameImage.width, holder.gameImage.height))
            .centerInside()
            .diskCacheStrategy(DiskCacheStrategy.ALL)
            .format(DecodeFormat.PREFER_RGB_565)
            .error(R.drawable.no_image_found)
            .into(holder.gameImage)

RecyclerView 视图回收代码

override fun onViewRecycled(holder: ViewHolder) {
    super.onViewRecycled(holder)
    Glide.with(holder.gameImage).clear(holder.gameImage)
}

加载150+条目后的内存占用

Glide内存占用


Fresco 实现细节

图片加载代码

val frescoRequest: ImageRequest = ImageRequestBuilder
            .newBuilderWithSource(itemViewModel.thumb.toUri())
            .setResizeOptions(ResizeOptions(holder.layout.width,holder.layout.height)).build()
        holder.gameImage.controller = Fresco.newDraweeControllerBuilder().setOldController(holder.gameImage.controller).setImageRequest(frescoRequest).build()

加载150+条目后的内存占用

Fresco内存占用


测试中发现的核心问题

  • 测试时Fresco用ARGB_8888格式,Glide用RGB_565格式
  • 两款库都把图片缩放到和ImageView相同尺寸,但Fresco内存更高、图片更清晰,Glide图片模糊
  • 尝试调整图片格式平衡内存与清晰度,但要么模糊,要么内存飙到250MB;另外内存分析器里近50MB的「Others」是什么原因?

解决方案与分析

一、修复Glide图片模糊问题

1. 调整图片解码格式

你用的DecodeFormat.PREFER_RGB_565是模糊的核心原因——这个格式每个像素仅占2字节,色彩精度远低于ARGB_8888(4字节)。要兼顾清晰度和内存:

  • 改用DecodeFormat.PREFER_ARGB_8888,同时限制Glide内存缓存大小避免暴涨:
    自定义Glide配置类:
    @GlideModule
    class CustomGlideModule : AppGlideModule() {
        override fun applyOptions(context: Context, builder: GlideBuilder) {
            // 根据设备内存设置合理缓存,比如20MB
            val cacheSize = 20 * 1024 * 1024 // 20MB
            builder.setMemoryCache(LruResourceCache(cacheSize.toLong()))
        }
    }
    
  • 如果坚持用RGB_565,必须确保缩放尺寸准确:holder.gameImage.width/height在onBindViewHolder时可能还没完成测量(返回0或默认值),导致图片错误缩放。可以监听控件测量完成后再加载:
    holder.gameImage.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            holder.gameImage.viewTreeObserver.removeOnGlobalLayoutListener(this)
            // 用实际测量后的尺寸加载图片
            Glide.with(holder.gameImage)
                .load(itemViewModel.thumb)
                .override(holder.gameImage.width, holder.gameImage.height)
                .format(DecodeFormat.PREFER_RGB_565)
                .into(holder.gameImage)
        }
    })
    

2. 更换缩放策略

当前的centerInside()会保持图片比例,若原图尺寸小于ImageView,会被拉伸导致模糊。可以换成:

  • fitCenter():保持比例,让图片完全覆盖ImageView(超出部分裁剪),原图尺寸足够时清晰度更好
  • centerCrop():裁剪图片中心区域适配ImageView,避免拉伸模糊,适合需要填充控件的场景

3. 开启内存缓存

你设置了skipMemoryCache(true),会导致每次滚动都要重新从磁盘解码图片,反而增加内存波动。建议关闭这个设置,让Glide缓存解码后的图片到内存,减少重复解码开销。


二、平衡内存与清晰度的通用方案

Glide侧优化

除上述调整外,还可以:

  • 启用DiskCacheStrategy.RESOURCE,只缓存解码后的图片,减少磁盘缓存的内存占用
  • 用thumbnail(0.5f)加载缩略图后再加载原图,提升滚动流畅度同时降低内存峰值

Fresco侧优化

如果想保留Fresco的清晰度同时降低内存:

  • 在ImageRequest中设置RGB_565格式:
    val decodeOptions = ImageDecodeOptions.newBuilder()
        .setPreferredBitmapConfig(Bitmap.Config.RGB_565)
        .build()
    val frescoRequest = ImageRequestBuilder
        .newBuilderWithSource(itemViewModel.thumb.toUri())
        .setImageDecodeOptions(decodeOptions)
        .setResizeOptions(ResizeOptions(holder.layout.width, holder.layout.height))
        .build()
    
  • 自定义Fresco内存缓存大小:
    val pipelineConfig = ImagePipelineConfig.newBuilder(context)
        .setBitmapMemoryCacheParamsSupplier {
            MemoryCacheParams(
                20 * 1024 * 1024, // 最大缓存20MB
                100, // 最大缓存条目数
                20 * 1024 * 1024,
                100,
                Long.MAX_VALUE
            )
        }
        .build()
    Fresco.initialize(context, pipelineConfig)
    

三、关于内存分析器「Others」的说明

「Others」通常包含以下几类内存:

  1. Native层内存:比如Fresco默认将图片缓存到Native内存,这部分不会计入Java堆,所以会归类到Others
  2. 未归类的临时对象:图片解码、磁盘缓存读写产生的临时内存对象
  3. 系统资源:未关闭的文件流、网络连接、JNI引用等
  4. 第三方库未识别内存:部分第三方库的内存分配未被Android Studio分析器归类

如果你的App中Others占50MB,大概率是Fresco的Native缓存导致的。可以通过以下方式排查:

  • 用Android Studio Profiler的「Native Memory」面板查看具体分配情况
  • 禁用磁盘缓存后观察Others是否减少,判断是否是缓存导致
  • 检查是否有未释放的资源(比如网络请求、文件流)引发内存泄漏

内容的提问来源于stack exchange,提问作者YOUSEF -ELSAYED

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 01:30:51