标签归档:Fresco

Fresco源代码分析之二:SimpleDraweeView如何拉取图片并绘制在屏幕上?

Fresco源代码分析之二:SimpleDraweeView如何拉取图片并绘制在屏幕上?

引言

《Fresco源代码分析之1:Fresco的初始化 》文章中,仔细分析了Fresco.init(Context)方法中所做的工作。总结下来有一下几点:

  1. 初始化了 ImagePipelineFactory , 包括默认的ImagePipelineConfig(其中初始化了各种线程池、是否解码、旋转之类的设置、各种Cache的默认配置等等)。
  2. 初始化了默认的 SimpleDraweeView ,包括对应的ImagePipeline。

那么问题来了,当我们通过SimpleDraweeView的

setImageUri(Uri, Object)

方法调用的时候,Fresco到底做了哪些工作呢?图片是经过了哪些工作绘制在界面上的呢?这篇文章就来说说这些事情。

调用方法

``` class="language-none">  /**
   * Displays an image given by the uri.
   *
   * @param uri uri of the image
   * @param callerContext caller context
   */
  public void setImageURI(Uri uri, @Nullable Object callerContext) {
    DraweeController controller = mSimpleDraweeControllerBuilder
        .setCallerContext(callerContext)
        .setUri(uri)
        .setOldController(getController())
        .build();
    setController(controller);
  }
```

在想象中,图片库要将图片显示出来,至少要经过查询缓存、网络获取、图片的解码、写入缓存、图片的后处理、显示图片这几个过程。但查看Fresco的代码,发现仅仅这几行,两句话就完事了,那么这两句话后面,到底有哪些故事发生呢?

源代码分析

从上述代码出发,分析图片加载的全过程。

获取DraweeController

代码的第一行,典型的是一个获取DraweeController的方法,具体的工作是怎么样的呢?

    DraweeController controller = mSimpleDraweeControllerBuilder
        .setCallerContext(callerContext)
        .setUri(uri)
        .setOldController(getController())
        .build();

其中mSimpleDraweeControllerBuilder是一个SimpleDraweeControllerBuilder的接口,该方法以此调用了四个方法,分别设置了 调用者的上下文Uri旧的Controller。从接口上看不出来Fresco具体做了什么工组的,因此还是要看看具体的实现。

在Fresco的初始化代码中,是这样初始化的

  // 初始化Drawee
  private static void initializeDrawee(Context context) {
    sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context);
    SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
  }

因此可以看出来,SimpleDraweeView所对应的Controller实际上是源自PipelineDraweeControllerBuilder,进一步查看,可以发现继承关系如下:

SimpleDraweeControllerBuilder
|
|
AbstractDraweeControllerBuilder
|
|
PipelineDraweeControllerBuilder

既然在SimpleDraweeView.setUri()方法中只调用了setCallerContext\setUri\setOldController,那么我们就从这四个方法入手

setCallerContext(Object callerContext)

这个方法源自 AbstractDraweeControllerBuilder

  /** Sets the caller context. */
  @Override
  public BUILDER setCallerContext(Object callerContext) {
    mCallerContext = callerContext;
    return getThis();
  }

和字面意思一样,该方法就是简单的设置了上下文,而且需要注意的是,上下文的类型为Object

setUri(Uri uri)

该方法首先调用 PipelineDraweeControllerBuilder 的对应的方法

  @Override
  public PipelineDraweeControllerBuilder setUri(Uri uri) {
    return super.setImageRequest(ImageRequest.fromUri(uri));
  }

其实本质上还是调用父类的这个方法,但需要注意的是,在这里面使用ImageRequest.fromUri(Uri)又将方法包装了一下,将Uri包装为ImageRequest的类型。ImageRequest的类的注释如下:

Immutable object encapsulating everything pipeline has to know about requested image to proceed.

可以直接看出,在Fresco中,ImageRequest为一个不可变类,其中包含了一个请求所需要的全部信息。

而调用至 AbstractDraweeControllerBuilder 中对应的方法也很简单,就是设置了一下对应的成员变量

  /** Sets the image request. */
  public BUILDER setImageRequest(REQUEST imageRequest) {
    mImageRequest = imageRequest;
    return getThis();
  }

setOldController(DraweeController)

此处也是调用的 AbstractDraweeControllerBuilder中的方法,具体代码如下:

  /** Sets the old controller to be reused if possible. */
  @Override
  public BUILDER setOldController(@Nullable DraweeController oldController) {
    mOldController = oldController;
    return getThis();
  }

工作也很简单,就是同样是设置了成员变量,那么这样看来,所有的工作应该都是放在了build()方法里面,现在去看看。

build()

build方法也是直接调用的 AbstractDraweeControllerBuilder中的方法,

  /** Builds the specified controller. */
  @Override
  public AbstractDraweeController build() {
    validate();

    // if only a low-res request is specified, treat it as a final request.
    if (mImageRequest == null && mMultiImageRequests == null && mLowResImageRequest != null) {
      mImageRequest = mLowResImageRequest;
      mLowResImageRequest = null;
    }

    return buildController();
  }

这里面有一个判断

仅仅在ImageRequest以及MultiImageRequest都为空,且LowResImagaeRequest(低质量请求)存在的时候,才将这个低质量的请求作为最终的请求来处理。

buildController() 方法做了什么呢?代码如下:

  /** Builds a regular controller. */
  protected AbstractDraweeController buildController() {
    AbstractDraweeController controller = obtainController();
    controller.setRetainImageOnFailure(getRetainImageOnFailure());
    maybeBuildAndSetRetryManager(controller);
    maybeAttachListeners(controller);
    return controller;
  }

可以看出来,分了4个步骤:

  1. 获取对应的Controller
  2. 配置Controller是否显示失败重新获取的图片
  3. 设置重试管理器(RetryManager)
  4. 设置Attach事件监听者

要想搞明白了,还是一件一件事情依次看看做了什么。

AbstractDraweeController controller = obtainController();

AbstractDraweeControllerBuilder中并没有obtainController()的默认实现,并要求子类实现,在PipelineDraweeControllerBuilder中,其实现如下:

    /**
     * 该方法会尝试复用以前的Controller,
     * 复用的办法是:
     * 1\首先判断Controller的类型,如果是PipelineDraweeController,则初始化即可
     * 2\如果不是如果是PipelineDraweeController的类型,则新建一个.
     * @return
     */

  @Override
  protected PipelineDraweeController obtainController() {
    DraweeController oldController = getOldController();
    PipelineDraweeController controller;
    if (oldController instanceof PipelineDraweeController) {
      controller = (PipelineDraweeController) oldController;
      controller.initialize(
          obtainDataSourceSupplier(),
          generateUniqueControllerId(),
          getCallerContext());
    } else {
      controller = mPipelineDraweeControllerFactory.newController(
          obtainDataSourceSupplier(),
          generateUniqueControllerId(),
          getCallerContext());
    }
    return controller;
  }

其实代码也很简单,就是检查之前通过setOldController设置的Controller是否为自身的类型,如果是,则通过设置重新使用,如果不是,则通过对应的工厂方法重新建立一个。

当然,我们不能看到这边就可以了,既然Controller可以复用,我们就看看重用Controller的初始化代码中都做了什么工作?

  /**
   * 通过新配置的Id和调用者上下文,重新初始化AbstractDraweeController
   * 这种设计允许在不需要重新实例化controller的时候复用之前的Controller.
   *
   * Initializes this controller with the new id and caller context.
   * This allows for reusing of the existing controller instead of instantiating a new one.
   * This method should be called when the controller is in detached state.
   * @param id unique id for this controller
   * @param callerContext tag and context for this controller
   */
  protected void initialize(String id, Object callerContext) {
    init(id, callerContext);
  }

  private void init(String id, Object callerContext) {
    // 记录事件:开始初始化Controller
    mEventTracker.recordEvent(Event.ON_INIT_CONTROLLER);
    // 如果之前有DeferredReleaser,则释放
    // cancel deferred release
    if (mDeferredReleaser != null) {
      mDeferredReleaser.cancelDeferredRelease(this);
    }
    // 重新初始化各种状态
    // reinitialize mutable state (fetch state)
    mIsAttached = false;
    releaseFetch();
    mRetainImageOnFailure = false;
    // 重新初始化RetryManager
    // reinitialize optional components
    if (mRetryManager != null) {
      mRetryManager.init();
    }
    // 重新初始化GestureDetector
    if (mGestureDetector != null) {
      mGestureDetector.init();
      mGestureDetector.setClickListener(this);
    }
    // 清空ControllerListener
    if (mControllerListener instanceof InternalForwardingListener) {
      ((InternalForwardingListener) mControllerListener).clearListeners();
    } else {
      mControllerListener = null;
    }
    // 清空Drawee视图
    // 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;
  }

上面的代码就是Controller初始化或者说重新初始化过程,所调用的代码,具体都有哪些工作呢?依次来看:

  1. 记录事件:初始化Controller
  2. 如果之前调用了DeferredReleaser,则取消。
  3. 设置变量 mISAttached = false; 释放之前的网络获取。
  4. 重新配置RetryManager.
  5. 重新设置触摸事件检测
  6. 配置MControllerListener,若为内置转发Listener,则保留,反之清空。
  7. 清空对应的Drawee视图。
  8. 设置Id和上下文。

setController(DraweeController)

setController(DraweeController)的代码如下:

  /** Sets the controller. */
  public void setController(@Nullable DraweeController draweeController) {
    mDraweeHolder.setController(draweeController);
    super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
  }

这一块代码也很简单,两句话:

  1. 为mDraweeHoler设置了Controller
  2. 调用了ImageView的setImageDrawable(Drawable)方法,设置了一张图片。

其实第2步代码的作用非常容易理解:就是在网络数据拉取成功之前,先设置一张默认的图片,这样在交互的时候,界面更加友好。 那么这样看来,网络请求发出等工作都是在第1步代码当中了。

  /**
   * 配置新的Controller
   * Sets a new controller.
   */
  public void setController(@Nullable DraweeController draweeController) {
    // 首先判断该DraweeHolder是否已经与某个Controller绑定.(按照Controller方法的调用行为,此处更应该表述的是,该图像已经需要Controller加载过)
    boolean wasAttached = mIsControllerAttached;
    // 如果之前绑定过,则首先解除绑定.
    if (wasAttached) {
      detachController();
    }

    // Clear the old controller, 重置之前的Controller
    if (mController != null) {
      mEventTracker.recordEvent(Event.ON_CLEAR_OLD_CONTROLLER);
      mController.setHierarchy(null);
    }
    // 将新的Controller赋值给成员变量.
    mController = draweeController;
    // 记录事件,配置视图
    if (mController != null) {
      mEventTracker.recordEvent(Event.ON_SET_CONTROLLER);
      mController.setHierarchy(mHierarchy);
    } else {
      mEventTracker.recordEvent(Event.ON_CLEAR_CONTROLLER);
    }

    // 如果之前的Controller曾经绑定,则进一步调用attachController
    if (wasAttached) {
      attachController();
    }
  }

setController 方法着实也非常简单,可以看出其实主要就是几个工作:

  1. 首先检查该DraweeHolder是否之前绑定过Controller,如果有,则调用detachController()方法释放之前的Controller。
  2. 清空之前的Controller,并将新的controller对象赋给mController。
  3. 记录事件,为新的controller配置视图。
  4. 如果该DraweeHolder之前绑定过Controller,则调用attachController()方法直接拉起下一步进程。

需要注意的是,我们在代码分析中可以发现,其实我们在setController的时候,Fresco并不一定会直接发出网络请求,那么Fresco是什么时候进行的呢?分为两种情况:

  1. 如果DraweeHoler之前有通过Controller加载过图片,则直接通过新的controller重新加载。
  2. 如果没有,则等待时机执行。那什么时候呢?接着分析一下。

图片的加载

其实很简单,Controller中有个方法叫onAttach(),这个名字是不是很熟悉?没错,和View的onAttach()方法签名一样,同样的,则个方法也是监听DraweeHolder的onAttach()回调函数,来看看。

ViewHolder

  /**
   * Gets the controller ready to display the image.
   *
   * <p>The containing view must call this method from both {@link View#onFinishTemporaryDetach()}
   * and {@link View#onAttachedToWindow()}.
   */
  public void onAttach() {
    mEventTracker.recordEvent(Event.ON_HOLDER_ATTACH);
    mIsHolderAttached = true;
    attachOrDetachController();
  }

代码很简单,就是记录事件,然后设置变量,然后调用下面一个方法,接着看。

  private void attachOrDetachController() {
    if (mIsHolderAttached && mIsVisible && mIsActivityStarted) {
      attachController();
    } else {
      detachController();
    }
  }

这个也不用多说,就是检查DraweeHolder是否已经已经在屏幕上了,是否是显示的,Activity是否已经启动,如果是,则直接attachController()。

AbastarctDraweeController 接下来就看看Controller的onAttach()方法了

  @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);
    // 检查mSettingableDraweeHierarchy是否为null
    Preconditions.checkNotNull(mSettableDraweeHierarchy);
    // 释放DeferredReleaser
    mDeferredReleaser.cancelDeferredRelease(this);
    // 设置mISAttached
    mIsAttached = true;
    // 如果任务没有提交,则提交
    if (!mIsRequestSubmitted) {
      submitRequest();
    }
  }

代码也很简单,无需多说,任务就是在这里被提交的。

  // 提交请求
  protected void submitRequest() {
    // 记录事件
    mEventTracker.recordEvent(Event.ON_DATASOURCE_SUBMIT);
    // 向Controller Listener 提交对应事件
    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();
    // 添加DataSubscriber
    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);
  }

提交请求的代码我也加了注释,但这个地方涉及到整个数据流的分析,后面接着讲,暂时就看到这里。

Fresco源代码分析:1、Fresco的初始化

Fresco源代码分析 之一 默认初始化

Fresco被认为是现在最为好用的Android图片加载库,在之前的文章,有根据官方文档分析过Fresco的入门手册,但使用始终无法明白其具体是怎么样工作的,因为,我们还是要从源码上分析Fresco的工作原理。

Fresco的初始化

在一般情况下,我们是使用默认的ImagePipeline配置来初始化Fresco的,代码如下:

  Fresco.initialize(this);

而实际上,Fresco一共提供了两个初始化方法


  /** Initializes Fresco with the default config. */
  public static void initialize(Context context) {
    // 初始化了ImagePipelineFactory的默认配置
    ImagePipelineFactory.initialize(context);
    initializeDrawee(context);
  }
  /** Initializes Fresco with the specified config. */
  public static void initialize(Context context, ImagePipelineConfig imagePipelineConfig) {
    ImagePipelineFactory.initialize(imagePipelineConfig);
    initializeDrawee(context);
  }

可以看到,这两个初始化方法仅仅是初始化ImagePipeline的不同,接下来依次分析。

初始化ImagePipelineFactory

同样的,ImagePipelineFactory总共提供了两个初始化方法,代码分别如下:

  /** Initializes {@link ImagePipelineFactory} with default config. */
  public static void initialize(Context context) {
    initialize(ImagePipelineConfig.newBuilder(context).build());
  }
  /** Initializes {@link ImagePipelineFactory} with the specified config. */
  public static void initialize(ImagePipelineConfig imagePipelineConfig) {
    sInstance = new ImagePipelineFactory(imagePipelineConfig);
  }

其中可以看到initialize(Context context)本质上也是调用initialize(ImagePipelineConfig)的方法,因此实际上也就是设置的ImagePipelineConfig的不同,而实际上,这个参数也是从Fresco的初始化Fresco.initialize(Context, ImagePipelineFactory)传入的。因此,我们从分析ImagePipelineConfig.newBuilder(context).build()开始。

ImagePipelineConfig初始化

ImagePipelineConfig.newBuilder(context)其实是构造了ImagePipelineConfig#Builder对象,build()方法则反悔了ImagePipelineConfig对象,其中设置的属性都有如下:

  @Nullable private final AnimatedImageFactory mAnimatedImageFactory; // 动画工厂
  private final Bitmap.Config mBitmapConfig; // Bitmap配置
  private final Supplier<MemoryCacheParams> mBitmapMemoryCacheParamsSupplier; // 缓存Bitmap所对应的MemoryCacheParams的供应者
  private final CacheKeyFactory mCacheKeyFactory; // CacheKey工厂
  private final Context mContext; // Context上下文
  private final boolean mDownsampleEnabled; // 是否运行下载缩略图
  private final boolean mDecodeFileDescriptorEnabled; // 是否允许解码文件描述否
  private final boolean mDecodeMemoryFileEnabled; // 是否允许解码内存中的文件
  private final Supplier<MemoryCacheParams> mEncodedMemoryCacheParamsSupplier; // 缓存未解码的图像所对应的MemoryCacheParams的供应者
  private final ExecutorSupplier mExecutorSupplier; // 线程池供应商
  private final ImageCacheStatsTracker mImageCacheStatsTracker; // 记录ImageCache各种状态的工具
  @Nullable private final ImageDecoder mImageDecoder; // 图片解码工具类
  private final Supplier<Boolean> mIsPrefetchEnabledSupplier; // 是否允许预取的供应商
  private final DiskCacheConfig mMainDiskCacheConfig; // 磁盘缓存配置
  private final MemoryTrimmableRegistry mMemoryTrimmableRegistry; // 监听内存状态
  private final NetworkFetcher mNetworkFetcher;  // 内存
  @Nullable private final PlatformBitmapFactory mPlatformBitmapFactory; // 不同版本对应的BitmapFactory
  private final PoolFactory mPoolFactory; // Pool的工厂
  private final ProgressiveJpegConfig mProgressiveJpegConfig; // 对于渐进式Jpeg的配置
  private final Set<RequestListener> mRequestListeners; // Request的监听者
  private final boolean mResizeAndRotateEnabledForNetwork; // 是否允许网络请求的图片设置大小或是旋转
  private final DiskCacheConfig mSmallImageDiskCacheConfig; // 小图片的磁盘缓存配置

ImagePipelineConfig内部的构造方法对本身复杂的参数进行设置,具体如下:

  private ImagePipelineConfig(Builder builder) {
    // 动画工厂
    mAnimatedImageFactory = builder.mAnimatedImageFactory;
    // Bitmap 内存缓存参数 Supplier
    mBitmapMemoryCacheParamsSupplier =
        builder.mBitmapMemoryCacheParamsSupplier == null ?
            new DefaultBitmapMemoryCacheParamsSupplier(
                (ActivityManager) builder.mContext.getSystemService(Context.ACTIVITY_SERVICE)) :
            builder.mBitmapMemoryCacheParamsSupplier;
    // 图片的默认格式为 ARGB_8888
    mBitmapConfig =
        builder.mBitmapConfig == null ?
            Bitmap.Config.ARGB_8888 :
            builder.mBitmapConfig;
    mCacheKeyFactory =
        builder.mCacheKeyFactory == null ?
            DefaultCacheKeyFactory.getInstance() :
            builder.mCacheKeyFactory;
    mContext = Preconditions.checkNotNull(builder.mContext);
    mDecodeFileDescriptorEnabled = builder.mDownsampleEnabled &&
        builder.mDecodeFileDescriptorEnabled;
    mDecodeMemoryFileEnabled = builder.mDecodeMemoryFileEnabled;
    mDownsampleEnabled = builder.mDownsampleEnabled;
    mEncodedMemoryCacheParamsSupplier =
        builder.mEncodedMemoryCacheParamsSupplier == null ?
            new DefaultEncodedMemoryCacheParamsSupplier() :
            builder.mEncodedMemoryCacheParamsSupplier;
    // 默认不记录Cache的Stat
    mImageCacheStatsTracker =
        builder.mImageCacheStatsTracker == null ?
            NoOpImageCacheStatsTracker.getInstance() :
            builder.mImageCacheStatsTracker;
    mImageDecoder = builder.mImageDecoder;
    mIsPrefetchEnabledSupplier =
        builder.mIsPrefetchEnabledSupplier == null ?
            new Supplier<Boolean>() {
              @Override
              public Boolean get() {
                return true;
              }
            } :
            builder.mIsPrefetchEnabledSupplier;
    // 磁盘默认 缓存大小配置
    mMainDiskCacheConfig =
        builder.mMainDiskCacheConfig == null ?
            getDefaultMainDiskCacheConfig(builder.mContext) :
            builder.mMainDiskCacheConfig;
    // 默认磁盘整理策略,该默认配置什么也不做
    mMemoryTrimmableRegistry =
        builder.mMemoryTrimmableRegistry == null ?
            NoOpMemoryTrimmableRegistry.getInstance() :
            builder.mMemoryTrimmableRegistry;
    // 默认的网络Fetcher:HttpURLConnection
    mNetworkFetcher =
        builder.mNetworkFetcher == null ?
            new HttpUrlConnectionNetworkFetcher() :
            builder.mNetworkFetcher;
    mPlatformBitmapFactory = builder.mPlatformBitmapFactory;
    mPoolFactory =
        builder.mPoolFactory == null ?
            new PoolFactory(PoolConfig.newBuilder().build()) :
            builder.mPoolFactory;
    mProgressiveJpegConfig =
        builder.mProgressiveJpegConfig == null ?
            new SimpleProgressiveJpegConfig() :
            builder.mProgressiveJpegConfig;
    mRequestListeners =
        builder.mRequestListeners == null ?
            new HashSet<RequestListener>() :
            builder.mRequestListeners;
    mResizeAndRotateEnabledForNetwork = builder.mResizeAndRotateEnabledForNetwork;
    mSmallImageDiskCacheConfig =
        builder.mSmallImageDiskCacheConfig == null ?
            mMainDiskCacheConfig :
            builder.mSmallImageDiskCacheConfig;
    // Below this comment can't be built in alphabetical order, because of dependencies
    int numCpuBoundThreads = mPoolFactory.getFlexByteArrayPoolMaxNumThreads();
    mExecutorSupplier =
        builder.mExecutorSupplier == null ?
            new DefaultExecutorSupplier(numCpuBoundThreads) : builder.mExecutorSupplier;
  }

实际上,对于ImagePipeline默认初始化的理解,就在于对这些参数设置的理解上,因此,接下来我们因此分析每一种配置的所表示的意义是什么。

Supplier<MemoryCacheParams> mBitmapMemoryCacheParamsSupplier

该配置用来表示默认的解码之后的Bitmap的缓存策略,其默认配置如下:

    // Bitmap 内存缓存参数 Supplier
    mBitmapMemoryCacheParamsSupplier =
        builder.mBitmapMemoryCacheParamsSupplier == null ?
            new DefaultBitmapMemoryCacheParamsSupplier(
                (ActivityManager) builder.mContext.getSystemService(Context.ACTIVITY_SERVICE)) :
            builder.mBitmapMemoryCacheParamsSupplier;

可以看到,如果默认情况下没有在builder中提供mBitmapMemoryCacheParamsSupplier中提供配置的话,会通过DefaultBitmapMemoryCacheParamsSupplier创建一个,那创建的内容是什么呢?DefaultBitmapMemoryCacheParamsSupplier代码比较简单,其配置的MemoryCacheParams内容通过表格简单列举一下。 MemoryCacheParams 配置:

变量名 默认值 备注
maxCacheSize 不同内存,cache大小不同,见下表 cache的最大空间,单位kb
maxCacheEntries 256 cache中允许的有效元素的最大数量
maxEvictionQueueSize Integer.MAX_VALUE cache待回收空间队列大小,单位kb
maxEvictionQueueEntries Integer.MAX_VALUE cache待回收队列最大元素数量
maxCacheEntrySize Integer.MAX_VALUE 单个cache所能容纳的最大元素数量

不同机器上,所配置的cache大小分别为:

序号 内存 设定值 说明
1 <32MB 4MB
2 >=32MB && <64MB 6MB
3 不在1、2之列 && API < 11 8MB 之前版本使用共享内存无法获得足够理想的效果
4 不再1、2之列 && API >= 11 程序可用Heap大小/4

CacheKeyFactory mCacheKeyFactory

CacheKeyFactory的配置代码如下:

    mCacheKeyFactory =
        builder.mCacheKeyFactory == null ?
            DefaultCacheKeyFactory.getInstance() :
            builder.mCacheKeyFactory;

对于CacheKeyFactory而言,其配置主要分为两类,一类是未解码的ImageRequest,一类是已经解码的ImageRequest,分别如下:

未解码的ImageRquest

对于这种ImageRquest,通过的SimpleCacheKey来实现。

  @Override
  public CacheKey getEncodedCacheKey(ImageRequest request) {
    return new SimpleCacheKey(getCacheKeySourceUri(request.getSourceUri()).toString());
  }

而实际上,SimpleCache则是简单的对传入的String取.hashCode(),换句话说,就是根据Uri生成了hashCode

解码的ImageRequest

剩余情况下,所使用的参数有:

参数名称 参数描述
mSourceString 一般对应Uri
mResizeOptions 大小调整参数
mAutoRotated 自动旋转参数
mImageDecodeOptions 解码配置
mPostprocessorCacheKey 后处理器CacheKey
mPostprocessorName 后处理器名称

Supplier<MemoryCacheParams> mEncodedMemoryCacheParams

该变量提供未解码图片缓存配置策略,代码如下:

    mEncodedMemoryCacheParamsSupplier =
        builder.mEncodedMemoryCacheParamsSupplier == null ?
            new DefaultEncodedMemoryCacheParamsSupplier() :
            builder.mEncodedMemoryCacheParamsSupplier;

看看DefaultEncodedMemoryCacheparamsSupplier都配置了什么?其实与CacheKeyFactory mCacheKeyFactory类似。区别在于Cache大小取值不同,具体如下:

序号 内存
<16MB 1MB
<32MB 2MB
>=64MB 4MB

ImageCacheStatsTracker mImageCacheStatsTracker

该配置主要用来控制ImageCache统计信息记录,相关代码如下:

    mImageCacheStatsTracker =
        builder.mImageCacheStatsTracker == null ?
            NoOpImageCacheStatsTracker.getInstance() :
            builder.mImageCacheStatsTracker;

默认配置为:什么ImageCache的统计信息也不记录。

DiskCacheConfig mMainDiskCacheConfig

磁盘缓存策略配置

    mMainDiskCacheConfig =
        builder.mMainDiskCacheConfig == null ?
            getDefaultMainDiskCacheConfig(builder.mContext) :
            builder.mMainDiskCacheConfig;

默认配置

  private static DiskCacheConfig getDefaultMainDiskCacheConfig(final Context context) {
    return DiskCacheConfig.newBuilder()
            // 默认文件路径
        .setBaseDirectoryPathSupplier(
            new Supplier<File>() {
              @Override
              public File get() {
                return context.getApplicationContext().getCacheDir();
              }
            })
            // 目录
        .setBaseDirectoryName("image_cache")
            // 默认大小 40MB
        .setMaxCacheSize(40 * ByteConstants.MB)
            // 磁盘空间不足时候,默认大小 10MB
        .setMaxCacheSizeOnLowDiskSpace(10 * ByteConstants.MB)
            // 磁盘控件极其不足时,默认 2MB
        .setMaxCacheSizeOnVeryLowDiskSpace(2 * ByteConstants.MB)
        .build();
  }

NetworkFetcher mNetworkFetcher

NetworkFetcher,Fresco支持okHttp和Android自带的HttpURLConnection实现。

    mNetworkFetcher =
        builder.mNetworkFetcher == null ?
            new HttpUrlConnectionNetworkFetcher() :
            builder.mNetworkFetcher;

网络的获取部分是一个图片库的重要部分,在后面我们需要重点分析,此处知道在默认情况下,Fresco采用Android自带的网络库即可。

PoolFactory mPoolFactory

在图形库当中,由于需要频繁小块的内存访问,重复申请空间会花费大量的时间,因此都会采用对象池/数据池的办法重复利用以前的对象,Fresco也不例外:

    mPoolFactory =
        builder.mPoolFactory == null ?
            new PoolFactory(PoolConfig.newBuilder().build()) :
            builder.mPoolFactory;

对应的默认配置代码如下:

  private PoolConfig(Builder builder) {
    mBitmapPoolParams =
        builder.mBitmapPoolParams == null ?
            DefaultBitmapPoolParams.get() :
            builder.mBitmapPoolParams;
    mBitmapPoolStatsTracker =
        builder.mBitmapPoolStatsTracker == null ?
            NoOpPoolStatsTracker.getInstance() :
            builder.mBitmapPoolStatsTracker;
    mFlexByteArrayPoolParams =
        builder.mFlexByteArrayPoolParams == null ?
            DefaultFlexByteArrayPoolParams.get() :
            builder.mFlexByteArrayPoolParams;
    mMemoryTrimmableRegistry =
        builder.mMemoryTrimmableRegistry == null ?
            NoOpMemoryTrimmableRegistry.getInstance() :
            builder.mMemoryTrimmableRegistry;
    mNativeMemoryChunkPoolParams =
        builder.mNativeMemoryChunkPoolParams == null ?
            DefaultNativeMemoryChunkPoolParams.get() :
            builder.mNativeMemoryChunkPoolParams;
    mNativeMemoryChunkPoolStatsTracker =
        builder.mNativeMemoryChunkPoolStatsTracker == null ?
            NoOpPoolStatsTracker.getInstance() :
            builder.mNativeMemoryChunkPoolStatsTracker;
    mSmallByteArrayPoolParams =
        builder.mSmallByteArrayPoolParams == null ?
            DefaultByteArrayPoolParams.get() :
            builder.mSmallByteArrayPoolParams;
    mSmallByteArrayPoolStatsTracker =
        builder.mSmallByteArrayPoolStatsTracker == null ?
            NoOpPoolStatsTracker.getInstance() :
            builder.mSmallByteArrayPoolStatsTracker;
  }

依赖来看:

PoolParams mBitmapPoolParams

默认情况下,Fresco不缓存任何Bitmap对象,如果使用完毕,则立刻释放。 PoolParams参数

名称 含义 默认配置
maxSizeHardCap 最大实际使用空间,当pool的size达到该设定值时,再申请空间会抛出BasePool.PoolSizeViolationException异常 内存>16MB -> 0.75Max;
内存<=16MB -> 0.5Max
maxSizeSoftCap 一个pool的虚拟容量:
当pool的size达到该设定值时,pool会尝试清理空间,直至size<该设定值 或者 空闲空间=0<
0:不缓存
bucketSizes pool可以包含各种各样的「size」,一个bucket用来表示一种大小,额外的,每一中bucket包含max-length,用来表示bucket中used+free的总共的元素数量,此处的maxSize是上面的soft类型的maxSize,如果达到限制,不会抛出异常,而仅仅是开始释放空间,如果此时仍有请求过来,则是和简单的alloc+free一样,不会被pool管理。如果该参数null,那么pool会根据需求自动创建bucket 0:不缓存
minBucketSize 表示pool中最小的bucket数量,可以保证任何元素小于等于该参数的都可以保存的bucket中 0
maxBucketSize 仅有元素size小于该参数时,才会保存至bucket中,如果尺寸超过该参数,会抛出异常。 Integer.MAX_VALUE

其他的也是各种Pool的配置,此处就不再多分析,需要的时候再过来看。

ProgressiveJpegConfig

渐进式照片显示控制。

RequestListeners

保存所有的请求监听者对象

ResizeAndRotateEnabledForNetwork

是否允许从网络获取的图像,调整大小/缩放

SmallImageDiskCacheConfig

较小图片的Disk Cache配置

ExecutorSupplier

线程池对象,注意,该对象初始化代码如下:

    int numCpuBoundThreads = mPoolFactory.getFlexByteArrayPoolMaxNumThreads();
    mExecutorSupplier =
        builder.mExecutorSupplier == null ?
            new DefaultExecutorSupplier(numCpuBoundThreads) : builder.mExecutorSupplier;

也就是说,这个是依赖于FlexByteArrayPoolMaxNumThreads()的,那么这个方法默认的参数是什么呢?跟进去看一下 FlexByteArrayPool中的线程池数量为:public static final int DEFAULT_MAX_NUM_THREADS = Runtime.getRuntime().availableProcessors();

初始化Drawee

Drawee的初始化代码如下:

  // 初始化Drawee
  private static void initializeDrawee(Context context) {
    sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context);
    SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
  }

可以看到Drawee的初始化分为两步:

  1. 实例化PipelineDraweeControllerBuilderSuppplier。
  2. 使用PipelineDraweeControllerBuilderSupplier初始化SimpleDraweeView。

PipelineDraweeControllerBuilderSupplier

该对象的实例过程如下:

  public PipelineDraweeControllerBuilderSupplier(
      Context context,
      ImagePipelineFactory imagePipelineFactory,
      Set<ControllerListener> boundControllerListeners) {
    mContext = context;
    mImagePipeline = imagePipelineFactory.getImagePipeline();
    mPipelineDraweeControllerFactory = new PipelineDraweeControllerFactory(
        context.getResources(),
        DeferredReleaser.getInstance(),
        imagePipelineFactory.getAnimatedDrawableFactory(),
        UiThreadImmediateExecutorService.getInstance());
    mBoundControllerListeners = boundControllerListeners;
  }

步骤如下:

  1. 获取ImagePipeline
  2. 实例化PipelineDraweeControllerFactory
  3. 设置Controller监听者

而PipelineDraweeController则设置了一下的属性

    // 资源所在的Resource
  private Resources mResources;
    // DeferredReleaser 用于释放任务
  private DeferredReleaser mDeferredReleaser;
    // 动画Drawable工厂
  private AnimatedDrawableFactory mAnimatedDrawableFactory;
    // 基于主线程的线程池
  private Executor mUiThreadExecutor;

其中变量的含义比较容易理解,就不再解释,需要注意的是, mUiThreadExecutor是一个封装了主线程Looper的Executor。

SimpleDraweeView的初始化

SimpleDraweeView的初始化更加简单,只有一行代码。

  public static void initialize(
      Supplier<? extends SimpleDraweeControllerBuilder> draweeControllerBuilderSupplier) {
    sDraweeControllerBuilderSupplier = draweeControllerBuilderSupplier;
  }

就是将前面初始化好的 PipelineDraweeControllerBuilderSupplier 设置给SimpleDraweeView.

好了,Fresco最基本的初始化过程就是这些,但这些还不能向我们解释,Fresco具体是将图片怎么样绘制到界面上的,在下一篇文章中,将对这些内容进行分析。