热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Fresco源码分析

一.项目介绍Fresco是Facebook提供的一个开源图片加载与管理库。它的功能很强大,可以从网络、本地存储和Android资源文件中加载图片,它完

一.项目介绍

Fresco是Facebook提供的一个开源图片加载与管理库。它的功能很强大,可以从网络、本地存储和Android资源文件中加载图片,它完全自己负责图片加载与显示,不需要你为细节去操心。
Fresco含有3级缓存设计(2级内存,1级文件)。

Android2.3及以上的系统都可以使用Fresco。在Android5.0以下的系统上,Fresco将图片放在一个特别的内存中(ashmem heap),而非Java Heap中,而5.0及以上的系统由于对内存的管理比之前的版本优化很多,所以图片缓存直接放在Java Heap中了。

二.简单用法(一个demo)
  1. 添加依赖
  2. 初始化Fresco
  3. 编写布局
  4. 指定Uri

布局中引入:

<com.facebook.drawee.view.SimpleDraweeViewandroid:id&#61;"&#64;&#43;id/my_image_view"android:layout_width&#61;"120dp"android:layout_height&#61;"wrap_content"fresco:viewAspectRatio&#61;"1"fresco:fadeDuration&#61;"300"fresco:actualImageScaleType&#61;"fitCenter"fresco:placeholderImage&#61;"&#64;mipmap/ic_launcher"fresco:failureImage&#61;"&#64;mipmap/ic_launcher"fresco:roundAsCircle&#61;"true"fresco:roundedCornerRadius&#61;"10dp"fresco:roundTopLeft&#61;"true"fresco:roundTopRight&#61;"true"fresco:roundBottomLeft&#61;"true"fresco:roundBottomRight&#61;"true"fresco:roundingBorderWidth&#61;"1dp"fresco:roundingBorderColor&#61;"#00ffff"/>

testIv.setImageURI("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png");

三.Fresco中的一些概念

1.DraweeView

继承于 View, 负责图片的显示。一般情况下&#xff0c;使用SimpleDraweeView 即可

2.ImageRequest

ImageRequest存储着Image Pipeline处理被请求图片所需要的有用信息(Uri、是否渐进式图片、是否返回缩略图、缩放、是否自动旋转等)。

3.监听下载事件

有时候我们需要监听图片显示的过程&#xff0c;比如在失败&#xff0c;中间过程&#xff0c;成功时做一些事情。我们可以这么做&#xff1a;

为SimpleDraweeView 指定一个 DraweeController
为DraweeController 指定一个 ControllerListener
在ControllerListener 的回调方法里处理 失败&#xff0c;中间过程&#xff0c;成功时的事情

Uri uri;
DraweeController controller &#61; Fresco.newControllerBuilder()
.setControllerListener(controllerListener)
.setUri(uri);
.build();
mSimpleDraweeView.setController(controller);

上面的代码指定了一个 ControllerListener &#xff0c;它包含一些回调方法&#xff1a;

onFinalImageSet 加载完成
onIntermediateImageSet 加载中间过程
onFailure 加载失败

4.Fresco对各种Uri类型的资源的支持

Fresco 支持许多URI格式。见下表&#xff1a;

类型 Scheme 示例
远程图片: http://, https:// HttpURLConnection 或者参考 使用其他网络加载方案
本地文件: file:// FileInputStream
Content provider: content:// ContentResolver
asset目录下的资源: asset:// AssetManager
res目录下的资源: res:// Resources.openRawResource

特别注意&#xff1a;Fresco 不支持 相对路径的URI. 所有的URI都必须是绝对路径&#xff0c;并且带上该URI的scheme。

四.总体设计

Fresco 是一个典型的 MVC 模型&#xff0c;只不过把 Model 叫做 DraweeHierarchy。

M : DraweeHierarchy
V : DraweeView
C : DraweeController

五.核心模块分述

1.视图层DraweeView继承体系及各个类的作用

DraweeView
–| GenericDraweeView
——| SimpleDraweeView

DraweeView (Viewer)

获取和设置Hierarchy&#43;Controller&#xff0c;DraweeView的相关信息在DraweeHolder中
DraweeHolder是一个辅助的类&#xff0c;解耦的设计方式&#xff0c;将需要设置以及传递控制的信息&#xff0c;全部交给DrawHolder来实现

/** This method is idempotent so it only has effect the first time it&#39;s called */private void init(Context context) {if (mInitialised) {return;}mInitialised &#61; true;mDraweeHolder &#61; DraweeHolder.create(null, context);if (Build.VERSION.SDK_INT >&#61; Build.VERSION_CODES.LOLLIPOP) {ColorStateList imageTintList &#61; getImageTintList();if (imageTintList &#61;&#61; null) {return;}setColorFilter(imageTintList.getDefaultColor());}}

再查看剩余的DraweeView的程序&#xff0c;发现其均将只是将相关事件传递给DraweeHolder&#xff0c;这是一种解耦的设计方式&#xff0c;以后就是不采用DraweeView&#xff0c;采用其他的方式&#xff0c;照样可以使用这套逻辑

GenericDraweeView

解析在xml中设置的属性

protected void inflateHierarchy(Context context, &#64;Nullable AttributeSet attrs) {GenericDraweeHierarchyBuilder builder &#61;GenericDraweeHierarchyInflater.inflateBuilder(context, attrs);setAspectRatio(builder.getDesiredAspectRatio());setHierarchy(builder.build());}

具体看下inflateHierarchy方法&#xff0c;这个方法中主要做的是解析并设置xml属性&#xff0c;这里采用的是建造者模式GenericDraweeHierarchyBuilder。

SimpleDraweeView

从外界设置ConrolllerBuilderSupplier
可以设置ImageUri
核心的业务逻辑位于DraweeView中
在控件初始化时&#xff0c;初始化了一个DraweeHolder

我们看下它的setImageURI方法的实现&#xff1a;

public void setImageURI(Uri uri, &#64;Nullable Object callerContext) {DraweeController controller &#61; mSimpleDraweeControllerBuilder.setCallerContext(callerContext).setUri(uri).setOldController(getController()).build();setController(controller);}

2.控制层DraweeController继承体系以及个各类的作用

DraweeController
–| AbstractDraweeController
—-| PipelineDraweeController

DraweeController:

获取和设置Hieraychy
view的各种事件通知过来&#xff0c;controller来控制这些逻辑的操作&#xff08;onAttach/onDetach/onTouchEvent/getAnimatable&#xff09;

AbstractDraweeController

最关键的功能&#xff1a; 实现了客户端向服务端的提交请求&#xff0c;即向DataSource中注册观察者&#xff0c;在有结果返回的时候&#xff0c;在主线程通知客户端更新即可&#xff0c;即设置Hierarychy的drawable即可
参照之前的分析方式&#xff0c;仍然采用先构造&#xff0c;然后具体方法的顺序

2.1 构造方法&#xff0c;设置了UI线程池&#xff0c;重试&#xff0c;以及手势相关的信息

public AbstractDraweeController(DeferredReleaser deferredReleaser,Executor uiThreadImmediateExecutor,String id,Object callerContext) {mDeferredReleaser &#61; deferredReleaser;mUiThreadImmediateExecutor &#61; uiThreadImmediateExecutor;init(id, callerContext, true);}

private void init(String id, Object callerContext, boolean justConstructed) {mEventTracker.recordEvent(Event.ON_INIT_CONTROLLER);// cancel deferred releaseif (!justConstructed && mDeferredReleaser !&#61; null) {mDeferredReleaser.cancelDeferredRelease(this);}// reinitialize mutable state (fetch state)mIsAttached &#61; false;mIsVisibleInViewportHint &#61; false;releaseFetch();mRetainImageOnFailure &#61; false;// reinitialize optional componentsif (mRetryManager !&#61; null) {mRetryManager.init();}if (mGestureDetector !&#61; null) {mGestureDetector.init();mGestureDetector.setClickListener(this);}if (mControllerListener instanceof InternalForwardingListener) {((InternalForwardingListener) mControllerListener).clearListeners();} else {mControllerListener &#61; null;}mControllerViewportVisibilityListener &#61; null;// clear hierarchy and controller overlayif (mSettableDraweeHierarchy !&#61; null) {mSettableDraweeHierarchy.reset();mSettableDraweeHierarchy.setControllerOverlay(null);mSettableDraweeHierarchy &#61; null;}mControllerOverlay &#61; null;// reinitialize constant stateif (FLog.isLoggable(FLog.VERBOSE)) {FLog.v(TAG, "controller %x %s -> %s: initialize", System.identityHashCode(this), mId, id);}mId &#61; id;mCallerContext &#61; callerContext;}

2.2 具体方法&#xff0c;在这里做分析时&#xff0c;我们重点关注图片如何获取&#xff0c;因而我们关注的核心方法是onAttach()&#xff0c;在这里实现了图片请求的机制&#xff0c;以及图片获取到如何回调&#xff0c;如何显示到UI层的控制&#xff0c;在下面的程序中&#xff0c;看到核心的设置的方法是submitRequest()

&#64;Overridepublic 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 &#61; true;if (!mIsRequestSubmitted) {submitRequest();}}

此处以第一次请求为例&#xff0c;这样分析比较简单&#xff0c;查看下面的方法&#xff0c;在请求时&#xff0c;设置请求的进度为0&#xff0c;获取到数据源(DataSource)&#xff0c;然后给数据源注册观察者(DataSubscriber)&#xff0c;先查看下面的SubmitRequest方法

protected void submitRequest() {final T closeableImage &#61; getCachedImage();if (closeableImage !&#61; null) {mDataSource &#61; null;mIsRequestSubmitted &#61; true;mHasFetchFailed &#61; 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 &#61; true;mHasFetchFailed &#61; false;mDataSource &#61; 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 &#61; mId;final boolean wasImmediate &#61; mDataSource.hasResult();final DataSubscriber dataSubscriber &#61;new BaseDataSubscriber() {&#64;Overridepublic void onNewResultImpl(DataSource dataSource) {// isFinished must be obtained before image, otherwise we might set intermediate result// as final image.boolean isFinished &#61; dataSource.isFinished();float progress &#61; dataSource.getProgress();T image &#61; dataSource.getResult();if (image !&#61; null) {onNewResultInternal(id, dataSource, image, progress, isFinished, wasImmediate);} else if (isFinished) {onFailureInternal(id, dataSource, new NullPointerException(), /* isFinished */ true);}}&#64;Overridepublic void onFailureImpl(DataSource dataSource) {onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);}&#64;Overridepublic void onProgressUpdate(DataSource dataSource) {boolean isFinished &#61; dataSource.isFinished();float progress &#61; dataSource.getProgress();onProgressUpdateInternal(id, dataSource, progress, isFinished);}};mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);}

到了这里&#xff0c;一次请求已经完成了&#xff0c;请求的结果会在回调中执行&#xff0c;但是请求是如何生成的呢&#xff1f;我们并没有看到具体发送请求的逻辑&#xff0c;这个疑问我们先记录下来&#xff08;暂且标记为Q1&#xff09;。先来看看对于请求结果是如何处理的&#xff0c;以新的一次请求结果为例&#xff0c;onNewResultImpl&#xff08;&#xff09;方法&#xff0c;而onNewResultImpl方法&#xff0c;以image不为空为例&#xff0c;最终会调用AbstractDraweeController.onNewResultInternal()方法。下面我们来看看&#xff0c;是如何处理这次新的请求的结果。

1.判断是否是想要的数据源&#xff0c;即查看数据信息是否是当前请求的信息&#xff0c;如果不是&#xff0c;直接释放了资源
2.如果是想要的数据源&#xff0c;创建对应的drawable&#xff0c;设置当前显示的drawable&#xff0c;释放之前缓存的drawable对象和Image对象

private void onNewResultInternal(String id,DataSource dataSource,&#64;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 &#64; onNewResult", image);releaseImage(image);dataSource.close();return;}mEventTracker.recordEvent(isFinished ? Event.ON_DATASOURCE_RESULT : Event.ON_DATASOURCE_RESULT_INT);// create drawableDrawable drawable;try {drawable &#61; createDrawable(image);} catch (Exception exception) {logMessageAndImage("drawable_failed &#64; onNewResult", image);releaseImage(image);onFailureInternal(id, dataSource, exception, isFinished);return;}T previousImage &#61; mFetchedImage;Drawable previousDrawable &#61; mDrawable;mFetchedImage &#61; image;mDrawable &#61; drawable;try {// set the new imageif (isFinished) {logMessageAndImage("set_final_result &#64; onNewResult", image);mDataSource &#61; 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 &#64; 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 !&#61; null && previousDrawable !&#61; drawable) {releaseDrawable(previousDrawable);}if (previousImage !&#61; null && previousImage !&#61; image) {logMessageAndImage("release_previous_result &#64; onNewResult", previousImage);releaseImage(previousImage);}}}

好了&#xff0c;就是获取到图像后续的操作&#xff0c;这个其实就是我们UI的操作&#xff0c;分析到此即可&#xff0c;其他的情况&#xff0c;我们参照这个分析的方式分析即可。下面我们来解决一下之前的Q1问题,数据源的请求是如何发送出去的&#xff0c;这个问题就比较复杂了&#xff0c;我们需要通过至少四篇的博客来分析这个请求的过程。

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的源码&#xff0c;发现其为接口&#xff0c;并且只有一个方法&#xff0c;就是用于获取顶层的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.* &#64;return top level drawable*/Drawable getTopLevelDrawable();
}

SettableDraweeHierachy&#xff1a;

图像可以被重置
图像可以设置进度
设置失败
设置重试
设置controllerOverlay
在理解获取顶层的Drawable时&#xff0c;需要首先理解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这里使用了建造者模式&#xff0c;为Fresco提供非常多的可配置选项。

这其中配置了比较核心的几项

mBitmapMemoryCacheParamsSupplier 内存缓存数据的策略
mCacheKeyFactory 缓存键值对的获取
mExecutorSupplier 获取本地读写线程池&#xff0c;网络数据线程池&#xff0c;解码线程池&#xff0c;以及后台线程池
mImageDecoder 解码器
网络数据获取器
......
ImagePipeLineConfig是一个比较核心的类&#xff0c;通过这个&#xff0c;我们可以得知&#xff0c;Freco初始化时&#xff0c;配置了大量的策略&#xff0c;可配置项很多&#xff0c;也就让我们的使用更加灵活和易于拓展

5.Fresco客户端与服务端的交互

以上我们分析了一遍&#xff0c;但是并未提到网络请求的位置的方式&#xff0c;具体的网络请求是在哪里呢&#xff1f;

我们回去看AbstractDraweeController.submitRequest()方法&#xff0c;当时我们并未说明getDataSource()方法是如何实现的。

&#64;Overrideprotected DataSource> 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的构造方法&#xff1a;

public PipelineDraweeController(Resources resources,DeferredReleaser deferredReleaser,AnimatedDrawableFactory animatedDrawableFactory,Executor uiThreadExecutor,MemoryCache memoryCache,Supplier>> dataSourceSupplier,String id,CacheKey cacheKey,Object callerContext) {super(deferredReleaser, uiThreadExecutor, id, callerContext);mResources &#61; resources;mAnimatedDrawableFactory &#61; animatedDrawableFactory;mMemoryCache &#61; memoryCache;mCacheKey &#61; cacheKey;init(dataSourceSupplier);}

private void init(Supplier>> dataSourceSupplier) {mDataSourceSupplier &#61; dataSourceSupplier;}

那么现在需要考虑的就是构造方法在哪里进行的调用,Alt&#43;F7快捷键查看调用的地方,发现只有一处地方,就是我们的PipelineDraweeControllerFactory.newController()的方法,而这个方法我们之前已经分析过了
,现在稍微总结一下:
就是说每次在生成请求的时候,即AbstractDraweeController在submitRequest的时候,都会去向Supplier中获取到数据源DataSource,这个数据源是在AbstractDraweeControllerBuilder.getDataSourceSupplierForRequest()中的get方法中获取到的数据源,所以已经将服务端的supplier的生成和客户端的supplier的使用已经结合了起来.
但是还是没看到请求是怎么发送出去的,然后请求回来的数据是如何更新UI的.
在前面我们已经分析了请求回来的数据是如何更新的,这个是通过给DataSource订阅观察者,然后去更新UI数据,这个具体的细节,我们之后再分析,这个已经超出了我们要分析的如何发送请求的范围

在AbstractDraweeControllerBuider的buildController方法&#xff1a;

/** Builds a regular controller. */protected AbstractDraweeController buildController() {AbstractDraweeController controller &#61; obtainController();controller.setRetainImageOnFailure(getRetainImageOnFailure());controller.setContentDescription(getContentDescription());controller.setControllerViewportVisibilityListener(getControllerViewportVisibilityListener());maybeBuildAndSetRetryManager(controller);maybeAttachListeners(controller);return controller;}

obtainController的具体实现在PipelineDraweeControllerBuilder中&#xff1a;

protected PipelineDraweeController obtainController() {DraweeController oldController &#61; getOldController();PipelineDraweeController controller;if (oldController instanceof PipelineDraweeController) {controller &#61; (PipelineDraweeController) oldController;controller.initialize(obtainDataSourceSupplier(),generateUniqueControllerId(),getCacheKey(),getCallerContext());} else {controller &#61; mPipelineDraweeControllerFactory.newController(obtainDataSourceSupplier(),generateUniqueControllerId(),getCacheKey(),getCallerContext());}return controller;}

其中调用到了AbstractDraweeControllerBuilder的obtainDataSourceSupplier方法&#xff1a;

/** Gets the top-level data source supplier to be used by a controller. */protected Supplier> obtainDataSourceSupplier() {if (mDataSourceSupplier !&#61; null) {return mDataSourceSupplier;}Supplier> supplier &#61; null;// final image supplier;if (mImageRequest !&#61; null) {supplier &#61; getDataSourceSupplierForRequest(mImageRequest);} else if (mMultiImageRequests !&#61; null) {supplier &#61; getFirstAvailableDataSourceSupplier(mMultiImageRequests, mTryCacheOnlyFirst);}// increasing-quality supplier; highest-quality supplier goes firstif (supplier !&#61; null && mLowResImageRequest !&#61; null) {List>> suppliers &#61; new ArrayList<>(2);suppliers.add(supplier);suppliers.add(getDataSourceSupplierForRequest(mLowResImageRequest));supplier &#61; IncreasingQualityDataSourceSupplier.create(suppliers);}// no image requests; use null data source supplierif (supplier &#61;&#61; null) {supplier &#61; DataSources.getFailedDataSourceSupplier(NO_REQUEST_EXCEPTION);}return supplier;}

具体这个请求是怎么发送出去的呢?之前有讲到在DraweeController的onAttach中获取了dataSource并且订阅了观察者,用于处理datasource返回的结果.来看一下实现中这个dataSource到底是怎么获取的。

我们再看一下getDataSourceSupplierForRequest方法&#xff1a;

/** Creates a data source supplier for the given image request. */protected Supplier> getDataSourceSupplierForRequest(final REQUEST imageRequest,final CacheLevel cacheLevel) {final Object callerContext &#61; getCallerContext();return new Supplier>() {&#64;Overridepublic DataSource get() {return getDataSourceForRequest(imageRequest, callerContext, cacheLevel);}&#64;Overridepublic String toString() {return Objects.toStringHelper(this).add("request", imageRequest.toString()).toString();}};}

这里调用到了getDataSourceForRequest方法&#xff0c;该方法在PipelineDraweeControllerBuilder中

&#64;Overrideprotected DataSource> getDataSourceForRequest(ImageRequest imageRequest,Object callerContext,CacheLevel cacheLevel) {return mImagePipeline.fetchDecodedImage(imageRequest,callerContext,convertCacheLevelToRequestLevel(cacheLevel));}

这里主要是通过调用了mImagePipeline.fetchDecodedImage方法&#xff0c;可见是通过Pipeline来获取的这个datasource.其内部是发起了一个submitFetchRequest返回一个DataSource.Pipeline也是Fresco一个重要的组成部分。下面会单独说&#xff1a;

6.Pipeline模块

简单来说pipeline就是实现了三级缓存,解码,变形等等,完成了提供可呈现图片的所有工作.Facebook官方中已经说明,ImagePipeline负责完成加载图像,并且将结果反馈(以回调或者说观察者的方式)出来.

这里先解释几个概念

Producer: 为了实现业务的隔离而设计的接口; 将pipeline中需要进行的每一项任务作为一个producer,通常将前一个producer作为参数传递个下一个producer,从而实现面向接口的业务模块剪的隔离.
Consumer: producer生产的结果会最终传递到consumer中,再通过实现了DataSource接口的适配器通知外部

public DataSource> fetchDecodedImage(ImageRequest imageRequest,Object callerContext,ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) {try {Producer> producerSequence &#61;mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);return submitFetchRequest(producerSequence,imageRequest,lowestPermittedRequestLevelOnSubmit,callerContext);} catch (Exception exception) {return DataSources.immediateFailedDataSource(exception);}}

我们去具体的看mProducerSequenceFactory中的代码&#xff1a;

/*** Submits a request for execution and returns a DataSource representing the pending decoded image(s).*

The returned DataSource must be closed once the client has finished with it.* &#64;param imageRequest the request to submit* &#64;return a DataSource representing the pending decoded image(s)*/
public DataSource> fetchDecodedImage(ImageRequest imageRequest,Object callerContext) {try {//有两块,下面的代码会分别进行分析//1.首先获取 producerSequence: 解码图片的请求队列Producer> producerSequence &#61;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> getDecodedImageProducerSequence(ImageRequest imageRequest) {//继续看getBasicDecodedImageSequence的实现Producer> pipelineSequence &#61;getBasicDecodedImageSequence(imageRequest);if (imageRequest.getPostprocessor() !&#61; null) {return getPostprocessorSequence(pipelineSequence);} else {return pipelineSequence;}
}//从这里就可以清楚地看到,针对不同的uri类型生成了不同的FetchSequence也就是Producer
private Producer> getBasicDecodedImageSequence(ImageRequest imageRequest) {Preconditions.checkNotNull(imageRequest);Uri uri &#61; imageRequest.getSourceUri();//判空Preconditions.checkNotNull(uri, "Uri is null.");//是否是网络请求if (UriUtil.isNetworkUri(uri)) {return getNetworkFetchSequence();} else if (UriUtil.isLocalFileUri(uri)) {//是否是本地videoif (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设计思想

在该代码阅读过程中&#xff0c;我参考了很多资料&#xff0c;最终才对这个流程稍微的有点认识&#xff0c;这个开源项目代码完整度非常高&#xff0c;必须站在一个比较高的设计思考上去思考才能理解得更好一些&#xff0c;我的目的也仅仅是对主流程实现的理解&#xff0c;其他很多的细节点并未去阅读&#xff0c;有赖于未来需要时再去进一步的学习。

参考资料

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


推荐阅读
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 手指触控|Android电容屏幕驱动调试指南
    手指触控|Android电容屏幕驱动调试指南 ... [详细]
  • 在处理大图片时,PHP 常常会遇到内存溢出的问题。为了避免这种情况,建议避免使用 `setImageBitmap`、`setImageResource` 或 `BitmapFactory.decodeResource` 等方法直接加载大图。这些函数在处理大图片时会消耗大量内存,导致应用崩溃。推荐采用分块处理、图像压缩和缓存机制等策略,以优化内存使用并提高处理效率。此外,可以考虑使用第三方库如 ImageMagick 或 GD 库来处理大图片,这些库提供了更高效的内存管理和图像处理功能。 ... [详细]
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • 在Conda环境中高效配置并安装PyTorch和TensorFlow GPU版的方法如下:首先,创建一个新的Conda环境以避免与基础环境发生冲突,例如使用 `conda create -n pytorch_gpu python=3.7` 命令。接着,激活该环境,确保所有依赖项都正确安装。此外,建议在安装过程中指定CUDA版本,以确保与GPU兼容性。通过这些步骤,可以确保PyTorch和TensorFlow GPU版的顺利安装和运行。 ... [详细]
  • Cisco OSI 软件更新指南:详细步骤与最佳实践
    Cisco OSI软件更新指南提供了详细的步骤和最佳实践,帮助用户高效、安全地完成软件升级。本文不仅涵盖了更新前的准备工作,还详细介绍了更新过程中的关键步骤和注意事项,确保系统稳定性和安全性。此外,还提供了故障排除和验证方法,以确保更新后的系统运行正常。 ... [详细]
  • 深入探索HTTP协议的学习与实践
    在初次访问某个网站时,由于本地没有缓存,服务器会返回一个200状态码的响应,并在响应头中设置Etag和Last-Modified等缓存控制字段。这些字段用于后续请求时验证资源是否已更新,从而提高页面加载速度和减少带宽消耗。本文将深入探讨HTTP缓存机制及其在实际应用中的优化策略,帮助读者更好地理解和运用HTTP协议。 ... [详细]
  • 本文深入探讨了Java多线程环境下的同步机制及其应用,重点介绍了`synchronized`关键字的使用方法和原理。`synchronized`关键字主要用于确保多个线程在访问共享资源时的互斥性和原子性。通过具体示例,如在一个类中使用`synchronized`修饰方法,展示了如何实现线程安全的代码块。此外,文章还讨论了`ReentrantLock`等其他同步工具的优缺点,并提供了实际应用场景中的最佳实践。 ... [详细]
  • Java能否直接通过HTTP将字节流绕过HEAP写入SD卡? ... [详细]
  • 提升Android开发效率:Clean Code的最佳实践与应用
    在Android开发中,提高代码质量和开发效率是至关重要的。本文介绍了如何通过Clean Code的最佳实践来优化Android应用的开发流程。以SQLite数据库操作为例,详细探讨了如何编写高效、可维护的SQL查询语句,并将其结果封装为Java对象。通过遵循这些最佳实践,开发者可以显著提升代码的可读性和可维护性,从而加快开发速度并减少错误。 ... [详细]
  • REST与RPC:选择哪种API架构风格?
    在探讨REST与RPC这两种API架构风格的选择时,本文首先介绍了RPC(远程过程调用)的概念。RPC允许客户端通过网络调用远程服务器上的函数或方法,从而实现分布式系统的功能调用。相比之下,REST(Representational State Transfer)则基于资源的交互模型,通过HTTP协议进行数据传输和操作。本文将详细分析两种架构风格的特点、适用场景及其优缺点,帮助开发者根据具体需求做出合适的选择。 ... [详细]
  • 本文深入探讨了HTTP头部中的Expires与Cache-Control字段及其缓存机制。Cache-Control字段主要用于控制HTTP缓存行为,其在HTTP/1.1中得到了广泛应用,而HTTP/1.0中主要使用Pragma:no-cache来实现类似功能。Expires字段则定义了资源的过期时间,帮助浏览器决定是否从缓存中读取资源。文章详细解析了这两个字段的具体用法、相互关系以及在不同场景下的应用效果,为开发者提供了全面的缓存管理指南。 ... [详细]
  • SQLite数据库CRUD操作实例分析与应用
    本文通过分析和实例演示了SQLite数据库中的CRUD(创建、读取、更新和删除)操作,详细介绍了如何在Java环境中使用Person实体类进行数据库操作。文章首先阐述了SQLite数据库的基本概念及其在移动应用开发中的重要性,然后通过具体的代码示例,逐步展示了如何实现对Person实体类的增删改查功能。此外,还讨论了常见错误及其解决方法,为开发者提供了实用的参考和指导。 ... [详细]
  • 在高清节目的高比特率传输过程中,使用外接USB硬盘进行时间平移(timeshift)时,出现了性能不足和流数据丢失的问题。通过深入研究,我们发现通过对图像组(GOP)和图像头(I-frame)的精确定位技术进行优化,可以显著提升系统的性能和稳定性。本研究提出了改进的图像组与图像头定位算法,有效减少了数据丢失,提高了流媒体传输的效率和质量。 ... [详细]
  • JVM参数设置与命令行工具详解
    JVM参数配置与命令行工具的深入解析旨在优化系统性能,通过合理设置JVM参数,确保在高吞吐量的前提下,有效减少垃圾回收(GC)的频率,进而降低系统停顿时间,提升服务的稳定性和响应速度。此外,本文还将详细介绍常用的JVM命令行工具,帮助开发者更好地监控和调优JVM运行状态。 ... [详细]
author-avatar
手机用户2502939795
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有