一.项目介绍
Fresco是Facebook提供的一个开源图片加载与管理库。它的功能很强大,可以从网络、本地存储和Android资源文件中加载图片,它完全自己负责图片加载与显示,不需要你为细节去操心。
Fresco含有3级缓存设计(2级内存,1级文件)。
Android2.3及以上的系统都可以使用Fresco。在Android5.0以下的系统上,Fresco将图片放在一个特别的内存中(ashmem heap),而非Java Heap中,而5.0及以上的系统由于对内存的管理比之前的版本优化很多,所以图片缓存直接放在Java Heap中了。
二.简单用法(一个demo)
- 添加依赖
- 初始化Fresco
- 编写布局
- 指定Uri
布局中引入:
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/my_image_view"
android:layout_width="120dp"
android:layout_height="wrap_content"
fresco:viewAspectRatio="1"
fresco:fadeDuration="300"
fresco:actualImageScaleType="fitCenter"
fresco:placeholderImage="@mipmap/ic_launcher"
fresco:failureImage="@mipmap/ic_launcher"
fresco:roundAsCircle="true"
fresco:roundedCornerRadius="10dp"
fresco:roundTopLeft="true"
fresco:roundTopRight="true"
fresco:roundBottomLeft="true"
fresco:roundBottomRight="true"
fresco:roundingBorderWidth="1dp"
fresco:roundingBorderColor="#00ffff"
/>
testIv.setImageURI("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png");
三.Fresco中的一些概念
1.DraweeView
继承于 View, 负责图片的显示。一般情况下,使用SimpleDraweeView 即可
2.ImageRequest
ImageRequest存储着Image Pipeline处理被请求图片所需要的有用信息(Uri、是否渐进式图片、是否返回缩略图、缩放、是否自动旋转等)。
3.监听下载事件
有时候我们需要监听图片显示的过程,比如在失败,中间过程,成功时做一些事情。我们可以这么做:
为SimpleDraweeView 指定一个 DraweeController
为DraweeController 指定一个 ControllerListener
在ControllerListener 的回调方法里处理 失败,中间过程,成功时的事情
Uri uri;
DraweeController controller = Fresco.newControllerBuilder()
.setControllerListener(controllerListener)
.setUri(uri);
.build();
mSimpleDraweeView.setController(controller);
上面的代码指定了一个 ControllerListener ,它包含一些回调方法:
onFinalImageSet 加载完成
onIntermediateImageSet 加载中间过程
onFailure 加载失败
4.Fresco对各种Uri类型的资源的支持
Fresco 支持许多URI格式。见下表:
类型 Scheme 示例
远程图片: http://, https:// HttpURLConnection 或者参考 使用其他网络加载方案
本地文件: file:// FileInputStream
Content provider: content:// ContentResolver
asset目录下的资源: asset:// AssetManager
res目录下的资源: res:// Resources.openRawResource
特别注意:Fresco 不支持 相对路径的URI. 所有的URI都必须是绝对路径,并且带上该URI的scheme。
四.总体设计
Fresco 是一个典型的 MVC 模型,只不过把 Model 叫做 DraweeHierarchy。
M : DraweeHierarchy
V : DraweeView
C : DraweeController
五.核心模块分述
1.视图层DraweeView继承体系及各个类的作用
DraweeView
–| GenericDraweeView
——| SimpleDraweeView
DraweeView (Viewer)
获取和设置Hierarchy+Controller,DraweeView的相关信息在DraweeHolder中
DraweeHolder是一个辅助的类,解耦的设计方式,将需要设置以及传递控制的信息,全部交给DrawHolder来实现
/** This method is idempotent so it only has effect the first time it's called */
private void init(Context context) {
if (mInitialised) {
return;
}
mInitialised = true;
mDraweeHolder = DraweeHolder.create(null, context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ColorStateList imageTintList = getImageTintList();
if (imageTintList == null) {
return;
}
setColorFilter(imageTintList.getDefaultColor());
}
}
再查看剩余的DraweeView的程序,发现其均将只是将相关事件传递给DraweeHolder,这是一种解耦的设计方式,以后就是不采用DraweeView,采用其他的方式,照样可以使用这套逻辑
GenericDraweeView
解析在xml中设置的属性
protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
GenericDraweeHierarchyBuilder builder =
GenericDraweeHierarchyInflater.inflateBuilder(context, attrs);
setAspectRatio(builder.getDesiredAspectRatio());
setHierarchy(builder.build());
}
具体看下inflateHierarchy方法,这个方法中主要做的是解析并设置xml属性,这里采用的是建造者模式GenericDraweeHierarchyBuilder。
SimpleDraweeView
从外界设置ConrolllerBuilderSupplier
可以设置ImageUri
核心的业务逻辑位于DraweeView中
在控件初始化时,初始化了一个DraweeHolder
我们看下它的setImageURI方法的实现:
public void setImageURI(Uri uri, @Nullable Object callerContext) {
DraweeController controller = mSimpleDraweeControllerBuilder
.setCallerContext(callerContext)
.setUri(uri)
.setOldController(getController())
.build();
setController(controller);
}
2.控制层DraweeController继承体系以及个各类的作用
DraweeController
–| AbstractDraweeController
—-| PipelineDraweeController
DraweeController:
获取和设置Hieraychy
view的各种事件通知过来,controller来控制这些逻辑的操作(onAttach/onDetach/onTouchEvent/getAnimatable)
AbstractDraweeController
最关键的功能: 实现了客户端向服务端的提交请求,即向DataSource中注册观察者,在有结果返回的时候,在主线程通知客户端更新即可,即设置Hierarychy的drawable即可
参照之前的分析方式,仍然采用先构造,然后具体方法的顺序
2.1 构造方法,设置了UI线程池,重试,以及手势相关的信息
public AbstractDraweeController(
DeferredReleaser deferredReleaser,
Executor uiThreadImmediateExecutor,
String id,
Object callerContext) {
mDeferredReleaser = deferredReleaser;
mUiThreadImmediateExecutor = uiThreadImmediateExecutor;
init(id, callerContext, true);
}
private void init(String id, Object callerContext, boolean justConstructed) {
mEventTracker.recordEvent(Event.ON_INIT_CONTROLLER);
// cancel deferred release
if (!justConstructed && mDeferredReleaser != null) {
mDeferredReleaser.cancelDeferredRelease(this);
}
// reinitialize mutable state (fetch state)
mIsAttached = false;
mIsVisibleInViewportHint = false;
releaseFetch();
mRetainImageOnFailure = false;
// reinitialize optional components
if (mRetryManager != null) {
mRetryManager.init();
}
if (mGestureDetector != null) {
mGestureDetector.init();
mGestureDetector.setClickListener(this);
}
if (mControllerListener instanceof InternalForwardingListener) {
((InternalForwardingListener) mControllerListener).clearListeners();
} else {
mControllerListener = null;
}
mControllerViewportVisibilityListener = null;
// clear hierarchy and controller overlay
if (mSettableDraweeHierarchy != null) {
mSettableDraweeHierarchy.reset();
mSettableDraweeHierarchy.setControllerOverlay(null);
mSettableDraweeHierarchy = null;
}
mControllerOverlay = null;
// reinitialize constant state
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(TAG, "controller %x %s -> %s: initialize", System.identityHashCode(this), mId, id);
}
mId = id;
mCallerContext = callerContext;
}
2.2 具体方法,在这里做分析时,我们重点关注图片如何获取,因而我们关注的核心方法是onAttach(),在这里实现了图片请求的机制,以及图片获取到如何回调,如何显示到UI层的控制,在下面的程序中,看到核心的设置的方法是submitRequest()
@Override
public void onAttach() {
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(
TAG,
"controller %x %s: onAttach: %s",
System.identityHashCode(this),
mId,
mIsRequestSubmitted ? "request already submitted" : "request needs submit");
}
mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
Preconditions.checkNotNull(mSettableDraweeHierarchy);
mDeferredReleaser.cancelDeferredRelease(this);
mIsAttached = true;
if (!mIsRequestSubmitted) {
submitRequest();
}
}
此处以第一次请求为例,这样分析比较简单,查看下面的方法,在请求时,设置请求的进度为0,获取到数据源(DataSource),然后给数据源注册观察者(DataSubscriber),先查看下面的SubmitRequest方法
protected void submitRequest() {
final T closeableImage = getCachedImage();
if (closeableImage != null) {
mDataSource = null;
mIsRequestSubmitted = true;
mHasFetchFailed = false;
mEventTracker.recordEvent(Event.ON_SUBMIT_CACHE_HIT);
getControllerListener().onSubmit(mId, mCallerContext);
onNewResultInternal(mId, mDataSource, closeableImage, 1.0f, true, true);
return;
}
mEventTracker.recordEvent(Event.ON_DATASOURCE_SUBMIT);
getControllerListener().onSubmit(mId, mCallerContext);
mSettableDraweeHierarchy.setProgress(0, true);
mIsRequestSubmitted = true;
mHasFetchFailed = false;
mDataSource = getDataSource();
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(
TAG,
"controller %x %s: submitRequest: dataSource: %x",
System.identityHashCode(this),
mId,
System.identityHashCode(mDataSource));
}
final String id = mId;
final boolean wasImmediate = mDataSource.hasResult();
final DataSubscriber<T> dataSubscriber =
new BaseDataSubscriber<T>() {
@Override
public void onNewResultImpl(DataSource<T> dataSource) {
// isFinished must be obtained before image, otherwise we might set intermediate result
// as final image.
boolean isFinished = dataSource.isFinished();
float progress = dataSource.getProgress();
T image = dataSource.getResult();
if (image != null) {
onNewResultInternal(id, dataSource, image, progress, isFinished, wasImmediate);
} else if (isFinished) {
onFailureInternal(id, dataSource, new NullPointerException(), /* isFinished */ true);
}
}
@Override
public void onFailureImpl(DataSource<T> dataSource) {
onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
}
@Override
public void onProgressUpdate(DataSource<T> dataSource) {
boolean isFinished = dataSource.isFinished();
float progress = dataSource.getProgress();
onProgressUpdateInternal(id, dataSource, progress, isFinished);
}
};
mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
}
到了这里,一次请求已经完成了,请求的结果会在回调中执行,但是请求是如何生成的呢?我们并没有看到具体发送请求的逻辑,这个疑问我们先记录下来(暂且标记为Q1)。先来看看对于请求结果是如何处理的,以新的一次请求结果为例,onNewResultImpl()方法,而onNewResultImpl方法,以image不为空为例,最终会调用AbstractDraweeController.onNewResultInternal()方法。下面我们来看看,是如何处理这次新的请求的结果。
1.判断是否是想要的数据源,即查看数据信息是否是当前请求的信息,如果不是,直接释放了资源
2.如果是想要的数据源,创建对应的drawable,设置当前显示的drawable,释放之前缓存的drawable对象和Image对象
private void onNewResultInternal(
String id,
DataSource<T> dataSource,
@Nullable T image,
float progress,
boolean isFinished,
boolean wasImmediate) {
// ignore late callbacks (data source that returned the new result is not the one we expected)
if (!isExpectedDataSource(id, dataSource)) {
logMessageAndImage("ignore_old_datasource @ onNewResult", image);
releaseImage(image);
dataSource.close();
return;
}
mEventTracker.recordEvent(
isFinished ? Event.ON_DATASOURCE_RESULT : Event.ON_DATASOURCE_RESULT_INT);
// create drawable
Drawable drawable;
try {
drawable = createDrawable(image);
} catch (Exception exception) {
logMessageAndImage("drawable_failed @ onNewResult", image);
releaseImage(image);
onFailureInternal(id, dataSource, exception, isFinished);
return;
}
T previousImage = mFetchedImage;
Drawable previousDrawable = mDrawable;
mFetchedImage = image;
mDrawable = drawable;
try {
// set the new image
if (isFinished) {
logMessageAndImage("set_final_result @ onNewResult", image);
mDataSource = null;
mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);
getControllerListener().onFinalImageSet(id, getImageInfo(image), getAnimatable());
// IMPORTANT: do not execute any instance-specific code after this point
} else {
logMessageAndImage("set_intermediate_result @ onNewResult", image);
mSettableDraweeHierarchy.setImage(drawable, progress, wasImmediate);
getControllerListener().onIntermediateImageSet(id, getImageInfo(image));
// IMPORTANT: do not execute any instance-specific code after this point
}
} finally {
if (previousDrawable != null && previousDrawable != drawable) {
releaseDrawable(previousDrawable);
}
if (previousImage != null && previousImage != image) {
logMessageAndImage("release_previous_result @ onNewResult", previousImage);
releaseImage(previousImage);
}
}
}
好了,就是获取到图像后续的操作,这个其实就是我们UI的操作,分析到此即可,其他的情况,我们参照这个分析的方式分析即可。下面我们来解决一下之前的Q1问题,数据源的请求是如何发送出去的,这个问题就比较复杂了,我们需要通过至少四篇的博客来分析这个请求的过程。
PipelineDraweeController
(以PipelineDraweeController为例)在通过builder.build()创建Controller的过程中,会调用obtainDataSourceSupplier来获取所需的DataSourceSupplier,进而在请求图片(submitRequest)的过程中获取当前的DataSource. 而通过在DataSource中订阅DataSourceSubscriber,使得请求到的数据在改变时能够通过controller将获取到的图片或者中间结果传递到DraweeHierarchy中,最终显示出来(…写得有点绕…x_x),对这个过程有个大概的了解有助于于之后DataSource模块进行连接。
PipelineDraweeController: Fresco默认的实现,也就是SimpleDraweeView中使用的, 用来桥接image pipeline和 SettableDraweeHierarchy
3.模型层DraweeHierachy继承体系以及各个类的作用
DraweeHierachy
用于获取顶层的drawable
先来看看DraweeHierachy的源码,发现其为接口,并且只有一个方法,就是用于获取顶层的Drawable
public interface DraweeHierarchy {
/**
* Returns the top level drawable in the corresponding hierarchy. Hierarchy should always have
* the same instance of its top level drawable.
* @return top level drawable
*/
Drawable getTopLevelDrawable();
}
SettableDraweeHierachy:
图像可以被重置
图像可以设置进度
设置失败
设置重试
设置controllerOverlay
在理解获取顶层的Drawable时,需要首先理解Drawable的继承结构
public interface SettableDraweeHierarchy extends DraweeHierarchy {
public void reset();
public void setImage(Drawable drawable, float progress, boolean immediate);
public void setProgress(float progress, boolean immediate);
public void setFailure(Throwable throwable);
public void setRetry(Throwable throwable);
public void setControllerOverlay(Drawable drawable);
}
4.Fresco初始化的过程
ImagePipelineFactory\ImagePipelineConfig
ImagePipelineConfig这里使用了建造者模式,为Fresco提供非常多的可配置选项。
这其中配置了比较核心的几项
mBitmapMemoryCacheParamsSupplier 内存缓存数据的策略
mCacheKeyFactory 缓存键值对的获取
mExecutorSupplier 获取本地读写线程池,网络数据线程池,解码线程池,以及后台线程池
mImageDecoder 解码器
网络数据获取器
......
ImagePipeLineConfig是一个比较核心的类,通过这个,我们可以得知,Freco初始化时,配置了大量的策略,可配置项很多,也就让我们的使用更加灵活和易于拓展
5.Fresco客户端与服务端的交互
以上我们分析了一遍,但是并未提到网络请求的位置的方式,具体的网络请求是在哪里呢?
我们回去看AbstractDraweeController.submitRequest()方法,当时我们并未说明getDataSource()方法是如何实现的。
@Override
protected DataSource<CloseableReference<CloseableImage>> getDataSource() {
if (FLog.isLoggable(FLog.VERBOSE)) {
FLog.v(TAG, "controller %x: getDataSource", System.identityHashCode(this));
}
return mDataSourceSupplier.get();
}
那么,这里就要考虑Supplier是从哪里初始化的,然后做了怎样的操作,查看当前的mDataSourceSupplier赋值的地方,得知,初始化的地方有两种方式,一种是构造,一种是直接的initialize,initialize的方法注释中已经告诉我们,这个是controller在detach的时候调用的,所以我们只需要查看构造即可
PipelineDraweeController的构造方法:
public PipelineDraweeController(
Resources resources,
DeferredReleaser deferredReleaser,
AnimatedDrawableFactory animatedDrawableFactory,
Executor uiThreadExecutor,
MemoryCache<CacheKey, CloseableImage> memoryCache,
Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier,
String id,
CacheKey cacheKey,
Object callerContext) {
super(deferredReleaser, uiThreadExecutor, id, callerContext);
mResources = resources;
mAnimatedDrawableFactory = animatedDrawableFactory;
mMemoryCache = memoryCache;
mCacheKey = cacheKey;
init(dataSourceSupplier);
}
private void init(Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier) {
mDataSourceSupplier = dataSourceSupplier;
}
那么现在需要考虑的就是构造方法在哪里进行的调用,Alt+F7快捷键查看调用的地方,发现只有一处地方,就是我们的PipelineDraweeControllerFactory.newController()的方法,而这个方法我们之前已经分析过了
,现在稍微总结一下:
就是说每次在生成请求的时候,即AbstractDraweeController在submitRequest的时候,都会去向Supplier中获取到数据源DataSource,这个数据源是在AbstractDraweeControllerBuilder.getDataSourceSupplierForRequest()中的get方法中获取到的数据源,所以已经将服务端的supplier的生成和客户端的supplier的使用已经结合了起来.
但是还是没看到请求是怎么发送出去的,然后请求回来的数据是如何更新UI的.
在前面我们已经分析了请求回来的数据是如何更新的,这个是通过给DataSource订阅观察者,然后去更新UI数据,这个具体的细节,我们之后再分析,这个已经超出了我们要分析的如何发送请求的范围
在AbstractDraweeControllerBuider的buildController方法:
/** Builds a regular controller. */
protected AbstractDraweeController buildController() {
AbstractDraweeController controller = obtainController();
controller.setRetainImageOnFailure(getRetainImageOnFailure());
controller.setContentDescription(getContentDescription());
controller.setControllerViewportVisibilityListener(getControllerViewportVisibilityListener());
maybeBuildAndSetRetryManager(controller);
maybeAttachListeners(controller);
return controller;
}
obtainController的具体实现在PipelineDraweeControllerBuilder中:
protected PipelineDraweeController obtainController() {
DraweeController oldController = getOldController();
PipelineDraweeController controller;
if (oldController instanceof PipelineDraweeController) {
controller = (PipelineDraweeController) oldController;
controller.initialize(
obtainDataSourceSupplier(),
generateUniqueControllerId(),
getCacheKey(),
getCallerContext());
} else {
controller = mPipelineDraweeControllerFactory.newController(
obtainDataSourceSupplier(),
generateUniqueControllerId(),
getCacheKey(),
getCallerContext());
}
return controller;
}
其中调用到了AbstractDraweeControllerBuilder的obtainDataSourceSupplier方法:
/** Gets the top-level data source supplier to be used by a controller. */
protected Supplier<DataSource<IMAGE>> obtainDataSourceSupplier() {
if (mDataSourceSupplier != null) {
return mDataSourceSupplier;
}
Supplier<DataSource<IMAGE>> supplier = null;
// final image supplier;
if (mImageRequest != null) {
supplier = getDataSourceSupplierForRequest(mImageRequest);
} else if (mMultiImageRequests != null) {
supplier = getFirstAvailableDataSourceSupplier(mMultiImageRequests, mTryCacheOnlyFirst);
}
// increasing-quality supplier; highest-quality supplier goes first
if (supplier != null && mLowResImageRequest != null) {
List<Supplier<DataSource<IMAGE>>> suppliers = new ArrayList<>(2);
suppliers.add(supplier);
suppliers.add(getDataSourceSupplierForRequest(mLowResImageRequest));
supplier = IncreasingQualityDataSourceSupplier.create(suppliers);
}
// no image requests; use null data source supplier
if (supplier == null) {
supplier = DataSources.getFailedDataSourceSupplier(NO_REQUEST_EXCEPTION);
}
return supplier;
}
具体这个请求是怎么发送出去的呢?之前有讲到在DraweeController的onAttach中获取了dataSource并且订阅了观察者,用于处理datasource返回的结果.来看一下实现中这个dataSource到底是怎么获取的。
我们再看一下getDataSourceSupplierForRequest方法:
/** Creates a data source supplier for the given image request. */
protected Supplier<DataSource<IMAGE>> getDataSourceSupplierForRequest(
final REQUEST imageRequest,
final CacheLevel cacheLevel) {
final Object callerContext = getCallerContext();
return new Supplier<DataSource<IMAGE>>() {
@Override
public DataSource<IMAGE> get() {
return getDataSourceForRequest(imageRequest, callerContext, cacheLevel);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("request", imageRequest.toString())
.toString();
}
};
}
这里调用到了getDataSourceForRequest方法,该方法在PipelineDraweeControllerBuilder中
@Override
protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
ImageRequest imageRequest,
Object callerContext,
CacheLevel cacheLevel) {
return mImagePipeline.fetchDecodedImage(
imageRequest,
callerContext,
convertCacheLevelToRequestLevel(cacheLevel));
}
这里主要是通过调用了mImagePipeline.fetchDecodedImage方法,可见是通过Pipeline来获取的这个datasource.其内部是发起了一个submitFetchRequest返回一个DataSource.Pipeline也是Fresco一个重要的组成部分。下面会单独说:
6.Pipeline模块
简单来说pipeline就是实现了三级缓存,解码,变形等等,完成了提供可呈现图片的所有工作.Facebook官方中已经说明,ImagePipeline负责完成加载图像,并且将结果反馈(以回调或者说观察者的方式)出来.
这里先解释几个概念
Producer: 为了实现业务的隔离而设计的接口; 将pipeline中需要进行的每一项任务作为一个producer,通常将前一个producer作为参数传递个下一个producer,从而实现面向接口的业务模块剪的隔离.
Consumer: producer生产的结果会最终传递到consumer中,再通过实现了DataSource接口的适配器通知外部
public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(
ImageRequest imageRequest,
Object callerContext,
ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) {
try {
Producer<CloseableReference<CloseableImage>> producerSequence =
mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
return submitFetchRequest(
producerSequence,
imageRequest,
lowestPermittedRequestLevelOnSubmit,
callerContext);
} catch (Exception exception) {
return DataSources.immediateFailedDataSource(exception);
}
}
我们去具体的看mProducerSequenceFactory中的代码:
/**
* Submits a request for execution and returns a DataSource representing the pending decoded image(s).
* <p>The returned DataSource must be closed once the client has finished with it.
* @param imageRequest the request to submit
* @return a DataSource representing the pending decoded image(s)
*/
public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(
ImageRequest imageRequest,
Object callerContext) {
try {
//有两块,下面的代码会分别进行分析
//1.首先获取 producerSequence: 解码图片的请求队列
Producer<CloseableReference<CloseableImage>> producerSequence =
mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
//2.
return submitFetchRequest(
producerSequence,
imageRequest,
ImageRequest.RequestLevel.FULL_FETCH,
callerContext);
} catch (Exception exception) {
return DataSources.immediateFailedDataSource(exception);
}
}
//******************************
//1.第一步 getDecodedImageProducerSequence
//******************************
//返回一个用于请求 解码图片的队列,可见这个队列是和imageRequest相关的,源码中,imageRequest是一个不可改变的JavaBean,其中包含了所有Pipeline请求图片所需要的所有信息.
public Producer<CloseableReference<CloseableImage>> getDecodedImageProducerSequence(
ImageRequest imageRequest) {
//继续看getBasicDecodedImageSequence的实现
Producer<CloseableReference<CloseableImage>> pipelineSequence =
getBasicDecodedImageSequence(imageRequest);
if (imageRequest.getPostprocessor() != null) {
return getPostprocessorSequence(pipelineSequence);
} else {
return pipelineSequence;
}
}
//从这里就可以清楚地看到,针对不同的uri类型生成了不同的FetchSequence也就是Producer
private Producer<CloseableReference<CloseableImage>> getBasicDecodedImageSequence(
ImageRequest imageRequest) {
Preconditions.checkNotNull(imageRequest);
Uri uri = imageRequest.getSourceUri();
//判空
Preconditions.checkNotNull(uri, "Uri is null.");
//是否是网络请求
if (UriUtil.isNetworkUri(uri)) {
return getNetworkFetchSequence();
} else if (UriUtil.isLocalFileUri(uri)) {
//是否是本地video
if (MediaUtils.isVideo(MediaUtils.extractMime(uri.getPath()))) {
return getLocalVideoFileFetchSequence();
} else {//本地图片
return getLocalImageFileFetchSequence();
}
} else if (UriUtil.isLocalContentUri(uri)) {
return getLocalContentUriFetchSequence();
} else if (UriUtil.isLocalAssetUri(uri)) {
return getLocalAssetFetchSequence();
} else if (UriUtil.isLocalResourceUri(uri)) {
return getLocalResourceFetchSequence();
} else if (UriUtil.isDataUri(uri)) {
return getDataFetchSequence();
} else {
//throw 异常
...
}
}
至此我们终于看到具体的网络请求和三级缓存的位置了。
六.阅读体会&优缺点&改进意见
采用了大量的设计模式
采用了生产者消费者模式
面向接口编程的思想非常浓
MVC设计思想
在该代码阅读过程中,我参考了很多资料,最终才对这个流程稍微的有点认识,这个开源项目代码完整度非常高,必须站在一个比较高的设计思考上去思考才能理解得更好一些,我的目的也仅仅是对主流程实现的理解,其他很多的细节点并未去阅读,有赖于未来需要时再去进一步的学习。
参考资料
Fresco简介
http://blog.v5.cn/2015/10/30/fresco%E7%AE%80%E4%BB%8B/
Fresco用法总结基础篇
http://www.lai18.com/content/9608860.html
Fresco之强大之余的痛楚
http://www.jianshu.com/p/5364957dcf49
Fresco源码解析 - Hierarchy / View / Controller
http://blog.csdn.net/feelang/article/details/45126421
Fresco 源码分析(一) DraweeView-DraweeHierarchy-DraweeController(MVC) DraweeView的分析
http://www.cnblogs.com/pandapan/p/4634563.html
Fresco的一点研究
http://frankls.cn/2015/12/02/study-of-fresco