Fresco源码分析

一.项目介绍

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.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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值