结合 Fresco 源码,详细讲解 SimpleDraweeView 的显示机制,分析其设计原理、实现细节、工作流程以及在图片加载和渲染中的作用。SimpleDraweeView 是 Fresco 提供的一个轻量级视图组件,用于高效加载和显示图片,集成了 Fresco 的图片加载管道(ImagePipeline)、缓存机制和渐进式渲染功能。
- SimpleDraweeView 概述
SimpleDraweeView 是 Fresco 的核心 UI 组件,继承自 GenericDraweeView,用于在 Android 应用中加载和显示图片。它封装了 Fresco 的复杂功能(如图片加载、缓存、渐进式渲染),为开发者提供简单易用的 API,同时支持高级特性(如占位图、失败图、圆角、渐进式 JPEG)。
核心功能:
- 图片加载:通过 ImagePipeline 从网络、缓存或本地加载图片。
- 渐进式渲染:支持渐进式 JPEG,逐步显示图片。
- 占位图和失败图:支持加载中、失败等状态的图片显示。
- 样式支持:支持圆形、圆角、缩放类型等 UI 定制。
- 内存管理:结合 CloseableReference,确保 Bitmap 安全释放。
源码位置:
-
定义在 com.facebook.drawee.view.SimpleDraweeView。
-
核心相关类:
- GenericDraweeView:父类,定义基础视图逻辑。
- DraweeController:控制图片加载和渲染。
- DraweeHierarchy:管理图片层级(如占位图、实际图片)。
- ImagePipeline:图片加载管道。
- CloseableReference:管理 Bitmap 内存。
- SimpleDraweeView 设计原理
SimpleDraweeView 的设计基于 MVC 模式(Model-View-Controller):
- Model:ImagePipeline 提供图片数据,管理加载和缓存。
- View:SimpleDraweeView 及其 DraweeHierarchy,负责图片渲染。
- Controller:DraweeController 协调数据加载和 UI 更新。
关键机制:
-
DraweeHierarchy:
- 管理图片层级(如占位图、实际图片、失败图),使用 Drawable 树结构。
- 支持动态切换图片(如渐进式渲染的低质量到高质量)。
-
DraweeController:
- 控制图片加载流程,与 ImagePipeline 交互。
- 处理加载状态(开始、进度、成功、失败)。
-
ImagePipeline:
- 提供图片数据,从缓存或网络获取。
- 支持渐进式 JPEG 和多级缓存。
-
CloseableReference:
- 管理解码后的 Bitmap,确保内存安全。
-
Event Handling:
- 通过 DataSubscriber 监听加载事件,更新 UI。
工作原理:
- 用户设置图片 URI,SimpleDraweeView 创建 ImageRequest 和 DraweeController。
- DraweeController 通过 ImagePipeline 加载图片,获取 CloseableReference。
- DraweeHierarchy 渲染图片,支持占位图、渐进式更新等。
- 加载完成后,释放资源,确保内存安全。
- SimpleDraweeView 显示流程
以下是 SimpleDraweeView 显示图片的完整流程,结合源码分析:
3.1 初始化
-
SimpleDraweeView 在布局或代码中初始化:
xml
<com.facebook.drawee.view.SimpleDraweeView android:id="@+id/image_view" android:layout_width="match_parent" android:layout_height="200dp" fresco:placeholderImage="@drawable/placeholder" /> -
构造方法初始化 DraweeHierarchy 和 DraweeController:
java
public class SimpleDraweeView extends GenericDraweeView { public SimpleDraweeView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } private void init(Context context, AttributeSet attrs) { if (!isInEditMode()) { DraweeHolder draweeHolder = DraweeHolder.create(null, context); setHierarchy(draweeHolder.getHierarchy()); setController(draweeHolder.getController()); } } } -
关键逻辑:
- DraweeHolder 管理 DraweeHierarchy 和 DraweeController。
- setHierarchy 设置图片层级,setController 设置加载控制器。
3.2 设置图片 URI
-
用户调用 setImageURI 触发图片加载:
java
simpleDraweeView.setImageURI("https://example.com/image.jpg"); -
源码:
java
public void setImageURI(String uriString) { ImageRequest request = ImageRequestBuilder.newBuilderWithSource(Uri.parse(uriString)) .build(); setController(Fresco.newDraweeControllerBuilder() .setImageRequest(request) .setOldController(getController()) .build()); } -
关键逻辑:
- 创建 ImageRequest,封装 URI 和加载选项(如渐进式渲染)。
- 使用 DraweeControllerBuilder 创建新的 DraweeController。
3.3 DraweeController 创建
-
AbstractDraweeControllerBuilder 创建 DraweeController:
java
public class PipelineDraweeControllerBuilder extends AbstractDraweeControllerBuilder { @Override public PipelineDraweeController build() { ImageRequest request = getImageRequest(); DataSource<CloseableReference<CloseableImage>> dataSource = mImagePipeline.fetchDecodedImage(request, getCallerContext()); return new PipelineDraweeController( getResourcesSupplier(), getDeferredReleaser(), mImagePipeline.getAnimatedDrawableFactory(), dataSource); } } -
关键逻辑:
- ImagePipeline 创建 DataSource 加载图片。
- PipelineDraweeController 管理加载和渲染流程。
3.4 图片加载
-
ImagePipeline 通过 Producer 链加载图片:
java
public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(ImageRequest request) { CacheKey cacheKey = mCacheKeyFactory.getBitmapCacheKey(request); CloseableReference<CloseableImage> cachedImage = mBitmapMemoryCache.get(cacheKey); if (cachedImage != null) { return ImmediateDataSource.create(cachedImage); } return mProducerSequenceFactory.getDecodedImageProducerSequence(request).produceResults(); } -
加载顺序:
- 检查 BitmapMemoryCache(Ashmem 或堆内存)。
- 检查 EncodedMemoryCache 和 DiskCache。
- 从网络或本地加载,解码为 CloseableImage。
3.5 渐进式渲染(可选)
-
如果启用渐进式 JPEG,ProgressiveJpegDecoder 逐步解码:
java
public CloseableImage decode(EncodedImage encodedImage, int length) { int nextScan = mJpegDecoder.getNextScanNumber(encodedImage.getPooledByteBuffer(), length); if (nextScan > mLastDecodedScan) { Bitmap bitmap = mJpegDecoder.decodeToBitmap(encodedImage, nextScan); mLastDecodedScan = nextScan; return new CloseableStaticBitmap(bitmap, bitmapReleaser); } return null; } -
DataSource 通知每次解码结果:
java
dataSource.subscribe(new BaseDataSubscriber<CloseableReference<CloseableImage>>() { @Override public void onNewResultImpl(DataSource<CloseableReference<CloseableImage>> dataSource) { handleNewResult(dataSource.getResult(), dataSource.getProgress()); } });
3.6 图片渲染
-
PipelineDraweeController 更新 DraweeHierarchy:
java
public class PipelineDraweeController extends AbstractDraweeController { @Override protected void onNewResultInternal( CloseableReference<CloseableImage> image, float progress, boolean isFinal) { if (CloseableReference.isValid(image)) { mSettableDraweeHierarchy.setImage(image.get(), progress, isFinal); } } } -
SettableDraweeHierarchy 设置图片:
java
public class SettableDraweeHierarchy implements DraweeHierarchy { private final FadeDrawable mFadeDrawable; public void setImage(CloseableImage image, float progress, boolean immediate) { Drawable drawable = image.getUnderlyingDrawable(); mFadeDrawable.setDrawable(drawable, progress, immediate); } } -
关键逻辑:
- FadeDrawable 管理图片层级(如占位图、实际图片)。
- setDrawable 更新图片,支持淡入效果。
3.7 资源释放
-
渲染完成后,释放 CloseableReference:
java
CloseableReference.closeSafely(imageRef); -
触发 Bitmap 和 Ashmem 内存释放:
java
ResourceReleaser<CloseableImage> releaser = image -> { if (image instanceof CloseableStaticBitmap) { ((CloseableStaticBitmap) image).close(); } };
- 底层原理与关键机制
SimpleDraweeView 的显示机制依赖以下核心机制,结合源码分析:
4.1 DraweeHierarchy
-
DraweeHierarchy 使用 Drawable 树结构管理图片层级:
java
public class GenericDraweeHierarchy implements DraweeHierarchy { private final FadeDrawable mFadeDrawable; private final Drawable mPlaceholderDrawable; private final Drawable mFailureDrawable; public GenericDraweeHierarchy(GenericDraweeHierarchyBuilder builder) { mPlaceholderDrawable = builder.getPlaceholderImage(); mFailureDrawable = builder.getFailureImage(); mFadeDrawable = new FadeDrawable(new Drawable[] { mPlaceholderDrawable, null, // 实际图片占位 mFailureDrawable }); } @Override public Drawable getTopLevelDrawable() { return mFadeDrawable; } } -
作用:
- 支持占位图、失败图和实际图片的动态切换。
- 使用 FadeDrawable 实现淡入效果。
4.2 DraweeController
-
PipelineDraweeController 协调加载和渲染:
java
public void submitRequest() { mDataSource = mImagePipeline.fetchDecodedImage(mImageRequest, mCallerContext); mDataSource.subscribe(new BaseDataSubscriber<CloseableReference<CloseableImage>>() { @Override public void onNewResultImpl(DataSource<CloseableReference<CloseableImage>> dataSource) { onNewResultInternal(dataSource.getResult(), dataSource.getProgress(), dataSource.isFinished()); } }, mUiThreadExecutor); } -
关键逻辑:
- 订阅 DataSource,监听加载进度和结果。
- 调用 SettableDraweeHierarchy.setImage 更新 UI。
4.3 ImagePipeline
-
ImagePipeline 提供图片数据,支持多级缓存和渐进式加载:
java
CloseableReference<CloseableImage> cachedImage = mBitmapMemoryCache.get(cacheKey); if (cachedImage != null) { return ImmediateDataSource.create(cachedImage); } -
缓存查询:
- BitmapMemoryCache(Ashmem 或堆内存)。
- EncodedMemoryCache 和 DiskCache。
4.4 CloseableReference
-
管理 CloseableImage(包含 Bitmap)的引用计数:
java
CloseableReference<CloseableImage> ref = CloseableReference.of(image, releaser); ref.close(); // 减少引用计数,可能释放 Bitmap -
确保内存安全,防止泄漏。
4.5 渐进式渲染
-
支持渐进式 JPEG,逐步更新图片:
java
mSettableDraweeHierarchy.setImage(image.get(), progress, false); -
progress 参数控制淡入效果,优化视觉体验。
- 优点与挑战
优点
-
简单易用:
- setImageURI 等 API 简化图片加载。
- 支持 XML 属性配置(如占位图、圆角)。
-
高性能:
- 集成 ImagePipeline,利用多级缓存和 Ashmem。
- 渐进式渲染优化慢网络体验。
-
灵活性:
- 支持圆形、圆角、缩放类型等样式。
- 可定制 DraweeHierarchy 和 DraweeController。
-
内存安全:
- CloseableReference 确保 Bitmap 正确释放。
-
渐进式支持:
- 动态更新图片质量,提升用户体验。
Challenges
-
复杂性:
- 内部依赖 DraweeHierarchy 和 ImagePipeline,调试较复杂。
- 需理解 Fresco 的异步机制。
-
内存管理:
- 需正确释放 CloseableReference,否则可能泄漏。
- Ashmem 和缓存配置需优化,适应低内存设备。
-
学习曲线:
- 相比 Glide 的 ImageView,Fresco 的 API 和配置更复杂。
-
性能开销:
- 渐进式渲染和多层 Drawable 可能增加 CPU 和内存使用。
- 与 Glide 的对比
为了更全面理解 SimpleDraweeView,以下将其与 Glide 的图片显示机制对比:
Glide 的图片显示
-
机制:
- 使用 ImageView 直接显示 Bitmap 或 Drawable。
- 通过 RequestBuilder 加载图片,渲染到 ImageView。
-
源码示例:
java
public class GlideRequest<TranscodeType> { public void into(ImageView view) { RequestManager requestManager = Glide.with(view); requestManager.load(model).apply(options).into(view); } } -
特点:
- 简单直接,依赖 Android 原生 ImageView。
- 不支持渐进式 JPEG,加载完成后一次性显示。
对比
| 特性 | SimpleDraweeView (Fresco) | ImageView (Glide) |
|---|---|---|
| 视图组件 | 自定义 DraweeView | 原生 ImageView |
| 渐进式加载 | 支持(ProgressiveJpegDecoder) | 不支持 |
| 内存管理 | CloseableReference、Ashmem | WeakReference、BitmapPool |
| 样式支持 | 圆形、圆角、占位图 | 需额外库(如 Glide Transformations) |
| 复杂性 | 较高(多组件协调) | 较低(简单 API) |
| 适用场景 | 复杂 UI、慢网络 | 标准加载、快速开发 |
- Fresco:适合复杂 UI 和慢网络场景,提供渐进式渲染和严格内存管理。
- Glide:简单易用,适合标准图片加载,但功能较局限。
- 优化建议
基于 SimpleDraweeView 的实现,以下是优化建议:
-
配置渐进式加载:
-
启用渐进式 JPEG,优化慢网络体验:
java
ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri) .setProgressiveRenderingEnabled(true) .build(); simpleDraweeView.setController(Fresco.newDraweeControllerBuilder() .setImageRequest(request) .build());
-
-
优化缓存:
-
调整 ImagePipelineConfig 的缓存大小:
java
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(context) .setBitmapMemoryCacheParamsSupplier(() -> new MemoryCacheParams( 20 * 1024 * 1024, 100, 10 * 1024 * 1024, 50, Integer.MAX_VALUE)) .build(); Fresco.initialize(context, config);
-
-
确保资源释放:
-
在 Activity 或 Fragment 销毁时释放 DraweeController:
java
@Override protected void onDestroy() { super.onDestroy(); simpleDraweeView.setController(null); }
-
-
自定义样式:
-
使用 XML 或代码配置圆角、占位图:
xml
<com.facebook.drawee.view.SimpleDraweeView fresco:roundedCornerRadius="10dp" fresco:placeholderImage="@drawable/placeholder" />java
GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(getResources()) .setRoundingParams(RoundingParams.asCircle()) .setPlaceholderImage(R.drawable.placeholder); simpleDraweeView.setHierarchy(builder.build());
-
-
监控性能:
-
使用 DraweeEventTracker 跟踪加载事件:
java
DraweeController controller = Fresco.newDraweeControllerBuilder() .setImageRequest(request) .setEventTracker(new DraweeEventTracker() { @Override public void onEvent(Event event) { Log.d("Drawee", "Event: " + event); } }) .build();
-
- 总结
SimpleDraweeView 是 Fresco 的核心视图组件,通过 DraweeHierarchy、DraweeController 和 ImagePipeline 实现高效的图片加载和显示。源码分析表明,其支持渐进式渲染、多级缓存、Ashmem 存储和严格内存管理,适合复杂 UI 和慢网络场景。与 Glide 的 ImageView 相比,SimpleDraweeView 提供更强大的功能,但复杂度较高。优化时需关注渐进式配置、缓存管理和资源释放。