Fresco 的图片加载流程

289 阅读5分钟

详细解析 Fresco 的图片加载流程及其背后的机制,Fresco 的图片加载流程围绕其核心模块(如 Drawee、ImagePipeline 和 Cache)展开,旨在高效加载、处理和显示图片,同时优化内存和性能。


Fresco 图片加载流程

Fresco 的图片加载流程可以分为以下几个主要阶段:

  1. 用户请求图片(DraweeController 和 DraweeView)

    • 流程起点是用户通过 SimpleDraweeView(或其父类 DraweeView)设置图片的 URI。SimpleDraweeView 是 Fresco 的 UI 组件,负责图片的显示。

    • 用户调用 SimpleDraweeView.setImageURI(Uri),最终会触发 DraweeController 创建一个图片请求。DraweeController 是 Drawee 层的中枢,协调图片加载和显示。

    • 源码示例(SimpleDraweeView):

      java

      public void setImageURI(Uri uri) {
          DraweeController controller = mDraweeHolder.createControllerForImage(uri);
          mDraweeHolder.setController(controller);
      }
      
    • DraweeHolder 管理 DraweeController 和 DraweeHierarchy,后者定义了图片的显示层级(如占位图、实际图片、失败图等)。

  2. 创建图片请求(ImageRequest)

    • DraweeController 将 URI 封装为一个 ImageRequest 对象,包含图片的来源、缩放规则、后处理等信息。

    • ImageRequest 是对图片加载任务的抽象,包含:

      • 图片的 URI(支持 HTTP、本地文件、资源等)。
      • 解码选项(DecodeOptions),如是否需要渐进式加载。
      • 后处理(Postprocessor),用于对图片进行自定义处理。
    • 源码示例(ImageRequestBuilder):

      java

      ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri)
          .setResizeOptions(new ResizeOptions(width, height))
          .setPostprocessor(postprocessor)
          .build();
      
  3. 提交请求到 ImagePipeline

    • DraweeController 将 ImageRequest 提交到 ImagePipeline,Fresco 的核心引擎,负责图片的获取、解码和缓存。

    • ImagePipeline 是一个异步、线程安全的组件,基于生产者-消费者模式,管理图片加载的整个生命周期。

    • 源码示例(ImagePipeline):

      java

      DataSource<CloseableReference<CloseableImage>> dataSource =
          mImagePipeline.fetchDecodedImage(imageRequest, callerContext);
      
    • DataSource 是一个异步结果的封装,允许订阅者(如 DraweeController)监听加载进度、结果或错误。

  4. 图片获取和缓存查询

    • ImagePipeline 首先查询缓存,Fresco 使用多级缓存机制:

      • 内存缓存(Bitmap Cache):存储解码后的 Bitmap,基于 MemoryCache 实现。
      • 编码缓存(Encoded Cache):存储未解码的图片数据(字节流),基于 DiskCache 或内存。
      • 磁盘缓存(Disk Cache):持久化存储图片数据,基于 DiskCache 实现,支持小图和大图分开存储。
    • 缓存查询顺序:内存缓存(Bitmap)→ 编码缓存 → 磁盘缓存 → 网络/本地加载。

    • 源码示例(ImagePipeline.fetchDecodedImage):

      java

      if (mBitmapMemoryCache.contains(cacheKey)) {
          return mBitmapMemoryCache.get(cacheKey);
      } else if (mEncodedMemoryCache.contains(cacheKey)) {
          return decodeFromEncodedImage(cacheKey);
      } else {
          return fetchFromDiskOrNetwork(cacheKey);
      }
      
  5. 图片下载和解码

    • 如果缓存未命中,ImagePipeline 通过 Producer 链获取图片数据:

      • NetworkFetcher:从网络下载图片(如 HTTP 请求)。
      • LocalFileFetchProducer:从本地文件读取。
      • 其他 Producer:如 ContentProvider 或 Asset。
    • 下载的图片数据(字节流)存储到编码缓存,随后由 ImageDecoder 解码为 Bitmap 或 CloseableImage。

    • Fresco 支持渐进式 JPEG 加载,允许图片部分数据到达时就开始解码和显示。

    • 源码示例(ProgressiveJpegDecoder):

      java

      while (hasMoreData()) {
          decodeNextScan();
          notifyConsumerWithPartialImage();
      }
      
  6. 图片后处理

    • 如果指定了 Postprocessor,ImagePipeline 会对解码后的图片执行后处理(如裁剪、滤镜)。

    • 后处理在 PostprocessorProducer 中执行,支持同步或异步操作。

    • 源码示例:

      java

      public class MyPostprocessor extends BasePostprocessor {
          @Override
          public void process(Bitmap bitmap) {
              // 自定义图片处理逻辑
          }
      }
      
  7. 图片显示

    • 解码后的图片(CloseableImage)通过 DataSource 回调到 DraweeController,并由 DraweeHierarchy 渲染到 DraweeView。

    • DraweeHierarchy 使用 SettableDraweeHierarchy,支持动态切换占位图、实际图片或错误图。

    • 源码示例(GenericDraweeHierarchy):

      java

      public void setImage(CloseableImage image, float progress, boolean immediate) {
          mActualImageDrawable.setImage(image);
          mFadeDrawable.setTransitionState(progress);
      }
      
  8. 资源管理和回收

    • Fresco 使用 CloseableReference 管理 Bitmap 和图片数据的生命周期,确保内存安全。

    • 当图片不再需要显示时,CloseableReference 会释放引用,触发垃圾回收。

    • 源码示例:

      java

      CloseableReference<CloseableImage> reference = dataSource.getResult();
      try {
          // 使用图片
      } finally {
          CloseableReference.closeSafely(reference);
      }
      

背后的机制与设计理念

Fresco 的图片加载流程背后蕴含了多个精心设计的机制,体现了其高性能和内存优化的核心优势:

  1. 异步和线程安全

    • ImagePipeline 使用 ExecutorService 管理线程池,将网络请求、磁盘 IO 和图片解码分配到不同线程,避免阻塞主线程。
    • 源码中的 Producer 链基于异步回调,解耦了图片加载的各个阶段。
  2. 多级缓存机制

    • Fresco 的缓存分为三层(Bitmap Cache、Encoded Cache、Disk Cache),通过 CacheKey 唯一标识资源。
    • CountingMemoryCache 实现内存缓存,支持 LRU 淘汰策略,优化内存使用。
    • 磁盘缓存基于 DiskCacheConfig,支持小图和大图分开存储,减少 IO 开销。
  3. 渐进式加载

    • Fresco 支持渐进式 JPEG,ProgressiveJpegDecoder 可以在图片数据未完全到达时逐步解码和显示,提升用户体验。
    • 源码中的 ProgressiveJpegConfig 允许用户自定义扫描次数和质量。
  4. 内存管理

    • Fresco 使用 CloseableReference 和 PooledByteBuffer 管理内存资源,避免内存泄漏。
    • 通过 ImagePipelineConfig 配置 Bitmap 池和缓冲区池,适配不同设备内存。
  5. 模块化设计

    • Fresco 的模块化体现在 Producer 和 Consumer 模式,允许开发者扩展功能(如自定义 NetworkFetcher 或 Postprocessor)。
    • 源码中的 PipelineDraweeController 支持动态调整图片加载策略。
  6. 错误处理和重试

    • ImagePipeline 支持自动重试机制,NetworkFetcher 可配置重试次数。
    • 错误通过 DataSource 的 onFailure 回调通知,DraweeController 显示失败占位图。

总结

Fresco 的图片加载流程从 SimpleDraweeView 的请求开始,经过 DraweeController 协调、ImageRequest 封装,提交到 ImagePipeline 执行缓存查询、图片下载、解码、后处理,最终渲染到 UI。整个流程高度异步,依托多级缓存、渐进式加载和严格的内存管理机制,实现了高效、稳定的图片加载。