Fresco源碼解析: 初始化過程(一)

簡介

以下是官網對fresco的定義:
fresco是一款功能強大的圖片加載工具,使用它之後,你不必再去關心圖片的加載和顯示這些繁瑣的事情!支持Android2.1及以後的版本。
中文官網地址如下:https://www.fresco-cn.org/,代碼下載地址:git clone https://github.com/facebook/fresco.git

特性

內存管理

在5.0以下系統,Fresco將圖片放到一個特別的內存區域。當然,在圖片不顯示的時候,佔用的內存會自動被釋放。這會使得APP更加流暢,減少因圖片內存佔用而引發的OOM。

圖片加載

支持WebP解碼,即使在早先對WebP支持不完善的Android系統上也能正常使用!

圖片的漸進式呈現

Android 本身的圖片庫不支持此格式,但是Fresco支持。使用時,和往常一樣,僅僅需要提供一個圖片的URI即可,剩下的事情,Fresco會處理。

動圖加載

加載Gif圖和WebP動圖在任何一個Android開發者眼裏看來都是一件非常頭疼的事情。每一幀都是一張很大的Bitmap,每一個動畫都有很多幀。Fresco讓你沒有這些煩惱,它處理好每一幀並管理好你的內存。

以上是copy官網的內容。好,廢話不多講,下面開始源碼解析。這篇博客主要講Fresco的初始化,在後面的博客中隨着本人的研究,會慢慢深入。

Fresco的初始化

官網的示例代碼如下

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Fresco.initialize(this);
    }
}

所以就從Fresco.initialize(this)開始看,Fresco的java文件路徑如下:

fresco\drawee-backends\drawee-pipeline\src\main\java\com\facebook\drawee\backends\pipeline\Fresco.java

initialize方法的代碼如下:

  public static void initialize(Context context) {
    initialize(context, null, null);
  }

可以看到最終是調用了

public static void initialize(Context context,@Nullable ImagePipelineConfigimagePipelineConfig,
@Nullable DraweeConfig draweeConfig)
public static void initialize(
      Context context,
      @Nullable ImagePipelineConfig imagePipelineConfig,
      @Nullable DraweeConfig draweeConfig) {
    if (sIsInitialized) {
      FLog.w(
          TAG,
          "Fresco has already been initialized! `Fresco.initialize(...)` should only be called " +
            "1 single time to avoid memory leaks!");
    } else {
      sIsInitialized = true;
    }
    // we should always use the application context to avoid memory leaks
    context = context.getApplicationContext();
    if (imagePipelineConfig == null) {
      ImagePipelineFactory.initialize(context);
    } else {
      ImagePipelineFactory.initialize(imagePipelineConfig);
    }
    initializeDrawee(context, draweeConfig);
  }

可以看到,如果已經被初始化過的話就直接返回。因爲我們是直接調用的Fresco.initialize(this),並沒有指定imagePipelineConfig,所以走到ImagePipelineFactory.initialize(context),這個方法是初始化ImagePipelineFactory的,後面用到的ImagePipeline就是有這個類產生。方法源碼如下:

  public static void initialize(Context context) {
    initialize(ImagePipelineConfig.newBuilder(context).build());
  }

此方法中新建了一個ImagePipelineConfig,這個對象很重要,因爲後面加載圖片用到的基本所有組件都是這裏指定。我們來具體看一下里面的內容,通過跟蹤code,發現最終是調用了ImagePipelineConfig的構造方法

 private ImagePipelineConfig(Builder builder) {
    // We have to build experiments before the rest
    //目前處於實驗狀態的一些選項,變動很大,官方建議不要修改這些選項的默認值
    mImagePipelineExperiments = builder.mExperimentsBuilder.build();
    //bitmap內存緩存參數的提供者,以MemoryCacheParams類的形式封裝
    mBitmapMemoryCacheParamsSupplier = builder.mBitmapMemoryCacheParamsSupplier == null ?
            new DefaultBitmapMemoryCacheParamsSupplier((ActivityManager) builder.mContext.getSystemService(Context.ACTIVITY_SERVICE)) :
            builder.mBitmapMemoryCacheParamsSupplier;
    //內存回收策略
    mBitmapMemoryCacheTrimStrategy = builder.mBitmapMemoryCacheTrimStrategy == null ? new BitmapMemoryCacheTrimStrategy() :
            builder.mBitmapMemoryCacheTrimStrategy;
   // bitmap的配置參數,Bitmap.Config默認使用ARGB_8888,相對於Glid更好內存
    mBitmapConfig = builder.mBitmapConfig == null ? Bitmap.Config.ARGB_8888 : builder.mBitmapConfig;
    //生成緩存key的工廠,如果不想使用默認的key生成方法,可以自定義這個factory
    mCacheKeyFactory = builder.mCacheKeyFactory == null ? DefaultCacheKeyFactory.getInstance() : builder.mCacheKeyFactory;
    mContext = Preconditions.checkNotNull(builder.mContext);
    //文件緩存的工廠
    mFileCacheFactory = builder.mFileCacheFactory == null ? new DiskStorageCacheFactory(new DynamicDefaultDiskStorageFactory()) :
        builder.mFileCacheFactory;
    mDownsampleEnabled = builder.mDownsampleEnabled;
    //未解碼的內存緩存參數 
    mEncodedMemoryCacheParamsSupplier = builder.mEncodedMemoryCacheParamsSupplier == null ? new DefaultEncodedMemoryCacheParamsSupplier() :
            builder.mEncodedMemoryCacheParamsSupplier;
            //圖片緩存統計跟蹤工具
    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;
     //網絡數據獲取的工具,默認使用HttpUrlConnectionNetworkFetcher,也可以自己定義
    mNetworkFetcher = builder.mNetworkFetcher == null ?  new HttpUrlConnectionNetworkFetcher() : builder.mNetworkFetcher;
    //根據不同android版本生成不同的bitmap的工廠,主要是bitmap內存存儲位置,5.0以前存儲在ashmem,5.0以後存在java heap中
    mPlatformBitmapFactory = builder.mPlatformBitmapFactory;
    //生產各種池的工廠
    mPoolFactory = builder.mPoolFactory == null ? new PoolFactory(PoolConfig.newBuilder().build()) : builder.mPoolFactory;
    //漸進式加載的配置參數
    mProgressiveJpegConfig = builder.mProgressiveJpegConfig == null ? new SimpleProgressiveJpegConfig() :builder.mProgressiveJpegConfig;
    //監聽request過程中的各種事件
    mRequestListeners = builder.mRequestListeners == null ? new HashSet<RequestListener>() : builder.mRequestListeners;
    mResizeAndRotateEnabledForNetwork = builder.mResizeAndRotateEnabledForNetwork;
    //小圖硬盤緩存的配置參數,對應於mMainDiskCacheConfig 
    mSmallImageDiskCacheConfig = builder.mSmallImageDiskCacheConfig == null ? mMainDiskCacheConfig :builder.mSmallImageDiskCacheConfig;
    //圖片解碼器的配置參數
    mImageDecoderConfig = builder.mImageDecoderConfig;
    // 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;
    // Here we manage the WebpBitmapFactory implementation if any
    //在不支持webp的android版本上(4.0開始支持簡單的webp,4.2.1後全面支持)支持解碼webp圖片的工廠,默認爲null
    WebpBitmapFactory webpBitmapFactory = mImagePipelineExperiments.getWebpBitmapFactory();
    if (webpBitmapFactory != null) {
      BitmapCreator bitmapCreator = new HoneycombBitmapCreator(getPoolFactory());
      setWebpBitmapFactory(webpBitmapFactory, mImagePipelineExperiments, bitmapCreator);
    } else {
      // We check using introspection only if the experiment is enabled
      if (mImagePipelineExperiments.isWebpSupportEnabled() &&
          WebpSupportStatus.sIsWebpSupportRequired) {
        webpBitmapFactory = WebpSupportStatus.loadWebpBitmapFactoryIfExists();
        if (webpBitmapFactory != null) {
          BitmapCreator bitmapCreator = new HoneycombBitmapCreator(getPoolFactory());
          setWebpBitmapFactory(webpBitmapFactory, mImagePipelineExperiments, bitmapCreator);
        }
      }
    }
  }

各個選項的意思我已經在代碼中註釋,後面的文章中還會繼續詳細說明這些選項。現在回到Fresco.initialize方法,在完成ImagePipelineFactory的初始化後,就開始Drawee的初始化

public static void initialize(
      Context context,
      @Nullable ImagePipelineConfig imagePipelineConfig,
      @Nullable DraweeConfig draweeConfig) {
    if (sIsInitialized) {
      FLog.w(
          TAG,
          "Fresco has already been initialized! `Fresco.initialize(...)` should only be called " +
            "1 single time to avoid memory leaks!");
    } else {
      sIsInitialized = true;
    }
    // we should always use the application context to avoid memory leaks
    context = context.getApplicationContext();
    if (imagePipelineConfig == null) {
      ImagePipelineFactory.initialize(context);
    } else {
      ImagePipelineFactory.initialize(imagePipelineConfig);
    }
    initializeDrawee(context, draweeConfig);
  }

initializeDrawee(context, draweeConfig)方法的代碼如下:

  private static void initializeDrawee(Context context,@Nullable DraweeConfig draweeConfig) {
    sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context, draweeConfig);
    SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
  }

可以看到先是new了一個PipelineDraweeControllerBuilderSupplier對象,在Fresco的源碼中你會看到很多類都以Supplier結尾,其實這些類都是實現了Supplier接口,該接口只有一個get()方法,作用就是提供一個特定類型的對象。所以PipelineDraweeControllerBuilderSupplier從名字就可以看出這個對象是用於提供PipelineDraweeControllerBuilder的,而PipelineDraweeControllerBuilder類的作用就是用於創建PipelineDraweeController,PipelineDraweeController的作用就是連接imagePipeline和 SettableDraweeHierarchy的橋樑,具體怎樣工作在後面的文章再說明。繼續看PipelineDraweeControllerBuilderSupplier(context, draweeConfig)構造方法,最後是調到了

  public PipelineDraweeControllerBuilderSupplier(
      Context context,
      ImagePipelineFactory imagePipelineFactory,
      Set<ControllerListener> boundControllerListeners,
      @Nullable DraweeConfig draweeConfig) {
    mContext = context;
    mImagePipeline = imagePipelineFactory.getImagePipeline();

    if (draweeConfig != null && draweeConfig.getPipelineDraweeControllerFactory() != null) {
      mPipelineDraweeControllerFactory = draweeConfig.getPipelineDraweeControllerFactory();
    } else {
      mPipelineDraweeControllerFactory = new PipelineDraweeControllerFactory();
    }
    mPipelineDraweeControllerFactory.init(
        context.getResources(),
        DeferredReleaser.getInstance(),
        imagePipelineFactory.getAnimatedDrawableFactory(context),
        UiThreadImmediateExecutorService.getInstance(),
        mImagePipeline.getBitmapMemoryCache(),
        draweeConfig != null
            ? draweeConfig.getCustomDrawableFactories()
            : null,
        draweeConfig != null
            ? draweeConfig.getDebugOverlayEnabledSupplier()
            : null);
    mBoundControllerListeners = boundControllerListeners;
  }

這個構造函數主要工作是new了一個PipelineDraweeControllerFactory對象,並初始化這個對象,在初始化過程中又實例化了很多必要組件,這個文章後面再講。PipelineDraweeControllerFactory是真正生產PipelineDraweeController的類,PipelineDraweeControllerBuilder只是對已有的PipelineDraweeController對象進行包裝。
在new 完PipelineDraweeControllerBuilderSupplier後,又調用了SimpleDraweeView.initialize()其實就是將PipelineDraweeControllerBuilderSupplier對象賦值給SimpeDraweeView的sDraweeControllerBuilderSupplier變量。

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

到這裏初始化就結束了。看着代碼不多,其實在新建各個組建的時候還是做了很多事情的,比如根據不同的平臺初始化不同的對象。下一篇文章我們就從頭開始,更細緻的看一下各個組建的創建過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章