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 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 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具体是将图片怎么样绘制到界面上的,在下一篇文章中,将对这些内容进行分析。