详细解析 Fresco 的图片加载流程及其背后的机制,Fresco 的图片加载流程围绕其核心模块(如 Drawee、ImagePipeline 和 Cache)展开,旨在高效加载、处理和显示图片,同时优化内存和性能。
Fresco 图片加载流程
Fresco 的图片加载流程可以分为以下几个主要阶段:
-
用户请求图片(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,后者定义了图片的显示层级(如占位图、实际图片、失败图等)。
-
-
创建图片请求(ImageRequest)
-
DraweeController 将 URI 封装为一个 ImageRequest 对象,包含图片的来源、缩放规则、后处理等信息。
-
ImageRequest 是对图片加载任务的抽象,包含:
- 图片的 URI(支持 HTTP、本地文件、资源等)。
- 解码选项(DecodeOptions),如是否需要渐进式加载。
- 后处理(Postprocessor),用于对图片进行自定义处理。
-
源码示例(ImageRequestBuilder):
java
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) .setResizeOptions(new ResizeOptions(width, height)) .setPostprocessor(postprocessor) .build();
-
-
提交请求到 ImagePipeline
-
DraweeController 将 ImageRequest 提交到 ImagePipeline,Fresco 的核心引擎,负责图片的获取、解码和缓存。
-
ImagePipeline 是一个异步、线程安全的组件,基于生产者-消费者模式,管理图片加载的整个生命周期。
-
源码示例(ImagePipeline):
java
DataSource<CloseableReference<CloseableImage>> dataSource = mImagePipeline.fetchDecodedImage(imageRequest, callerContext); -
DataSource 是一个异步结果的封装,允许订阅者(如 DraweeController)监听加载进度、结果或错误。
-
-
图片获取和缓存查询
-
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); }
-
-
图片下载和解码
-
如果缓存未命中,ImagePipeline 通过 Producer 链获取图片数据:
- NetworkFetcher:从网络下载图片(如 HTTP 请求)。
- LocalFileFetchProducer:从本地文件读取。
- 其他 Producer:如 ContentProvider 或 Asset。
-
下载的图片数据(字节流)存储到编码缓存,随后由 ImageDecoder 解码为 Bitmap 或 CloseableImage。
-
Fresco 支持渐进式 JPEG 加载,允许图片部分数据到达时就开始解码和显示。
-
源码示例(ProgressiveJpegDecoder):
java
while (hasMoreData()) { decodeNextScan(); notifyConsumerWithPartialImage(); }
-
-
图片后处理
-
如果指定了 Postprocessor,ImagePipeline 会对解码后的图片执行后处理(如裁剪、滤镜)。
-
后处理在 PostprocessorProducer 中执行,支持同步或异步操作。
-
源码示例:
java
public class MyPostprocessor extends BasePostprocessor { @Override public void process(Bitmap bitmap) { // 自定义图片处理逻辑 } }
-
-
图片显示
-
解码后的图片(CloseableImage)通过 DataSource 回调到 DraweeController,并由 DraweeHierarchy 渲染到 DraweeView。
-
DraweeHierarchy 使用 SettableDraweeHierarchy,支持动态切换占位图、实际图片或错误图。
-
源码示例(GenericDraweeHierarchy):
java
public void setImage(CloseableImage image, float progress, boolean immediate) { mActualImageDrawable.setImage(image); mFadeDrawable.setTransitionState(progress); }
-
-
资源管理和回收
-
Fresco 使用 CloseableReference 管理 Bitmap 和图片数据的生命周期,确保内存安全。
-
当图片不再需要显示时,CloseableReference 会释放引用,触发垃圾回收。
-
源码示例:
java
CloseableReference<CloseableImage> reference = dataSource.getResult(); try { // 使用图片 } finally { CloseableReference.closeSafely(reference); }
-
背后的机制与设计理念
Fresco 的图片加载流程背后蕴含了多个精心设计的机制,体现了其高性能和内存优化的核心优势:
-
异步和线程安全
- ImagePipeline 使用 ExecutorService 管理线程池,将网络请求、磁盘 IO 和图片解码分配到不同线程,避免阻塞主线程。
- 源码中的 Producer 链基于异步回调,解耦了图片加载的各个阶段。
-
多级缓存机制
- Fresco 的缓存分为三层(Bitmap Cache、Encoded Cache、Disk Cache),通过 CacheKey 唯一标识资源。
- CountingMemoryCache 实现内存缓存,支持 LRU 淘汰策略,优化内存使用。
- 磁盘缓存基于 DiskCacheConfig,支持小图和大图分开存储,减少 IO 开销。
-
渐进式加载
- Fresco 支持渐进式 JPEG,ProgressiveJpegDecoder 可以在图片数据未完全到达时逐步解码和显示,提升用户体验。
- 源码中的 ProgressiveJpegConfig 允许用户自定义扫描次数和质量。
-
内存管理
- Fresco 使用 CloseableReference 和 PooledByteBuffer 管理内存资源,避免内存泄漏。
- 通过 ImagePipelineConfig 配置 Bitmap 池和缓冲区池,适配不同设备内存。
-
模块化设计
- Fresco 的模块化体现在 Producer 和 Consumer 模式,允许开发者扩展功能(如自定义 NetworkFetcher 或 Postprocessor)。
- 源码中的 PipelineDraweeController 支持动态调整图片加载策略。
-
错误处理和重试
- ImagePipeline 支持自动重试机制,NetworkFetcher 可配置重试次数。
- 错误通过 DataSource 的 onFailure 回调通知,DraweeController 显示失败占位图。
总结
Fresco 的图片加载流程从 SimpleDraweeView 的请求开始,经过 DraweeController 协调、ImageRequest 封装,提交到 ImagePipeline 执行缓存查询、图片下载、解码、后处理,最终渲染到 UI。整个流程高度异步,依托多级缓存、渐进式加载和严格的内存管理机制,实现了高效、稳定的图片加载。