Android Fresco 筆記

一、簡(jiǎn)介使用

1.1 介紹

Fresco對(duì)于圖片的展示支持多種情況:backgroud image(背景圖)、placeholder image(占位圖)、actual image(加載的圖片)、progress bar image(進(jìn)度條)、retry image(重新加載的圖片)、failure image(失敗圖片)與overlay image(疊加圖)。

主頁(yè):Fresco官方文檔

1.2優(yōu)點(diǎn)

1、支持webp格式的圖片F(xiàn)resco是通過(guò)jni來(lái)實(shí)現(xiàn)支持WebP格式圖片。

2、5.0以下系統(tǒng):使用”ashmem”(匿名共享內(nèi)存)區(qū)域存儲(chǔ)Bitmap緩存,這樣Bitmap對(duì)象的創(chuàng)建、釋放將永遠(yuǎn)不會(huì)觸發(fā)GC,關(guān)于”ashmem”存儲(chǔ)區(qū)域,它是一個(gè)不在Java堆區(qū)的一片存儲(chǔ)內(nèi)存空間,它的管理由Linux內(nèi)核驅(qū)動(dòng)管理,不必深究,只要知道這塊存儲(chǔ)區(qū)域是別于堆內(nèi)存之外的一塊空間就行了,且這塊空間是可以多進(jìn)程共享的,GC的活動(dòng)不會(huì)影響到它。5.0以上系統(tǒng),由于內(nèi)存管理的優(yōu)化,所以對(duì)于5.0以上的系統(tǒng)Fresco將Bitmap緩存直接放到了堆內(nèi)存中。
3、使用了三級(jí)緩存:Bitmap緩存+未解碼圖片緩存+硬盤(pán)緩存。
其中前兩個(gè)就是內(nèi)存緩存,Bitmap緩存根據(jù)系統(tǒng)版本不同放在了不同內(nèi)存區(qū)域中,而未解碼圖片的緩存只在堆內(nèi)存中

1.3使用

Application中初始化

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

xml

 <com.facebook.drawee.view.SimpleDraweeView
   android:id="@+id/my_image_view"
  android:layout_width="130dp"
  android:layout_height="130dp"
  fresco:placeholderImage="@drawable/my_drawable"
/>

具體

  Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/fresco-logo.png");
  SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
  draweeView.setImageURI(uri);

二、原理分析

DraweeView采用的 是典型的mvc模式,
DraweeHierarchy 負(fù)責(zé)的是跟顯示相關(guān)的,
DraweeController 負(fù)責(zé)的是后臺(tái)相關(guān)的,
DraweeHolder主要是統(tǒng)籌兩者的,F(xiàn)resco的初始化方法執(zhí)行后會(huì)初始化相關(guān)配置。

以SimpleDraweeView為例,它的controller是PipelineDraweeController,F(xiàn)resco在初始化時(shí)初始化一個(gè)PipelineDraweeControllerBuilderSupplier來(lái)提供這個(gè)controller,DraweeHolder主要通過(guò)controller啟動(dòng)后臺(tái)服務(wù)獲取數(shù)據(jù),setController方法執(zhí)行后后調(diào)用controller的onAttach()方法,這個(gè)方法會(huì)提交請(qǐng)求并獲得數(shù)據(jù),后將數(shù)據(jù)返回給ui線(xiàn)程,

Fresco數(shù)據(jù)是封裝成Supplier<DataSource<IMAGE>> 的形式返回,而真正將數(shù)據(jù)傳遞回來(lái)的是通過(guò)ImagePipeline,這個(gè)ImagePipeline會(huì)在PipelineDraweeControllerBuilderSupplier的構(gòu)造器初始化創(chuàng)建一個(gè),它會(huì)在Fresco初始化方法中進(jìn)行初始化,SimpleDraweeView初始化方法中會(huì)調(diào)用PipelineDraweeControllerBuilderSupplier的get的方法將ImagePipeline返回,ImagePipeline有一個(gè)RequestListsner的成員變量,它管理這個(gè)一個(gè)監(jiān)聽(tīng)列表,請(qǐng)求監(jiān)聽(tīng)通過(guò)這個(gè)RequestListsner返回,而正確去獲取數(shù)據(jù)的時(shí)候是啟動(dòng)一個(gè)后臺(tái)服務(wù)進(jìn)行數(shù)據(jù)獲取的,整個(gè)數(shù)據(jù)獲取有分多個(gè)階段,F(xiàn)resco通過(guò)producer劃分這些階段,比如緩存獲取階段、編碼階段、網(wǎng)絡(luò)獲取階段,它會(huì)一個(gè)一個(gè)階段去獲取數(shù)據(jù),如果producer1沒(méi)有獲取到數(shù)據(jù)就交給producer2進(jìn)行獲取數(shù)據(jù)以此類(lèi)推下去,producer之間通過(guò)consumer返回?cái)?shù)據(jù),最終將數(shù)據(jù)返回到ui線(xiàn)程去。

2.1SimpleDraweeView的繼承關(guān)系

Object (java.lang)
 View (android.view)
    ImageView (android.widget)
        DraweeView (com.facebook.drawee.view)  根部DraweeView
            GenericDraweeView (com.facebook.drawee.view)  通用的DraweeView
                SimpleDraweeView (com.facebook.drawee.view) 最簡(jiǎn)單的DraweeView

2.2從 SimpleDraweeView.setImageURI(uri);

     public void setImageURI(Uri uri, @Nullable Object callerContext) {
         DraweeController controller =mControllerBuilder
        .setCallerContext(callerContext)
        .setUri(uri)
        .setOldController(getController())
        .build();

         setController(controller);
 }

可以從字面看出來(lái) 給當(dāng)前view 設(shè)置一個(gè)controller,一個(gè)DraweeView對(duì)應(yīng)一個(gè)DraweeController。而Controller是由build模式所創(chuàng)建,查看 mControllerBuilder 的賦值來(lái)源

  private void init(Context context, @Nullable AttributeSet attrs) {
  ...
  mControllerBuilder = sDraweecontrollerbuildersupplier.get();
  ...
  }

這是在SimpleDraweeView的構(gòu)造方法中會(huì)調(diào)用init()方法,繼續(xù)查看sDraweecontrollerbuildersupplier是什么,

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

initialize 是的調(diào)用位置, Fresco中的initializeDrawee()

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

 public static void initialize(Context context, @Nullable ImagePipelineConfig imagePipelineConfig,  @Nullable DraweeConfig draweeConfig) {
   ...
   initializeDrawee(context, draweeConfig);
   ...
 }

這是static方法,F(xiàn)resco在Application中執(zhí)行Fresco.initialize(context)里 initializeDrawee(context, draweeConfig);
初始化Drawee相關(guān)配置信息。所以可以先搞清楚

sDraweeControllerBuilderSupplier 是 PipelineDraweeControllerBuilderSupplier
mControllerBuilder 是 PipelineDraweeControllerBuilder
controller 是 PipelineDraweeController

2.3setController(controller)

public void setController(@Nullable DraweeController draweeController) {
    mDraweeHolder.setController(draweeController);
    super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}

這里將控制器傳進(jìn)了holder里面。
mDraweeHolder.setController(draweeController)里最終調(diào)用了controller的onAttach()方法,

     @Override
  public void onAttach() {
       ......
      mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
      Preconditions.checkNotNull(mSettableDraweeHierarchy);
      mDeferredReleaser.cancelDeferredRelease(this);
      mIsAttached = true;
      if (!mIsRequestSubmitted) {
      submitRequest();
      }
    }

最終執(zhí)行到 submitRequest();

2.4submitRequest();

  protected void submitRequest() {
      mEventTracker.recordEvent(Event.ON_DATASOURCE_SUBMIT);
      getControllerListener().onSubmit(mId, mCallerContext);
      mSettableDraweeHierarchy.setProgress(0, true);
      mIsRequestSubmitted = true;
      mHasFetchFailed = false;
     mDataSource = getDataSource();//獲取數(shù)據(jù)
    .......
      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();
        T image = dataSource.getResult();
        if (image != null) {
          onNewResultInternal(id, dataSource, image, 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);
      }
    };
    
    mDataSource.subscribe(dataSubscriber,mUiThreadImmediateExecutor);//這個(gè)將獲取的數(shù)據(jù)回調(diào)到ui線(xiàn)程 
}

這里可以看出來(lái)是 獲取到數(shù)據(jù),然后將數(shù)據(jù)提交到ui線(xiàn)程去。

2.5 如何獲取數(shù)據(jù)getDataSource()

 @Override
   protected DataSource<CloseableReference<CloseableImage>> getDataSource() {
      .....
     return mDataSourceSupplier.get();
   }

這里出現(xiàn)了一個(gè)mDataSourceSupplier,mDataSourceSupplier只有在兩個(gè)地方傳遞進(jìn)去,一個(gè)是構(gòu)造器,還有一個(gè)是initialize方法,而這個(gè)方法只有在PipelineDraweeControllerBuilder的obtainController()時(shí)調(diào)用到:

    private void init(Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier) {
        mDataSourceSupplier = dataSourceSupplier;
     }

 public void initialize( Supplier<DataSource<CloseableReference<CloseableImage>>> dataSourceSupplier, String id,
  CacheKey cacheKey, Object callerContext, @Nullable ImmutableList<DrawableFactory> customDrawableFactories,
  @Nullable ImageOriginListener imageOriginListener) {
  ...
   init(dataSourceSupplier);
  ...
 }

initialize() 方法是哪里調(diào)用的呢?所有的方法都是在PipelineDraweeControllerBuilder的 調(diào)用父類(lèi)方法Builder 里面處理的

    protected PipelineDraweeController obtainController() {
            try {
                    ...
        controller.initialize(obtainDataSourceSupplier(controller, controllerId),
      controllerId,
      getCacheKey(),
      getCallerContext(),
      mCustomDrawableFactories,
      mImageOriginListener);
      controller.initializePerformanceMonitoring(mImagePerfDataListener);
     return controller;
     } finally {
      ...
   }
 }

父類(lèi) AbstractDraweeControllerBuilder.java

  protected AbstractDraweeController buildController() {
        ...
   AbstractDraweeController controller = obtainController();
   controller.setRetainImageOnFailure(getRetainImageOnFailure());
    controller.setContentDescription(getContentDescription());
    controller.setControllerViewportVisibilityListener(getControllerViewportVisibilityListener());
    ...
    return controller;
 }

  @Override
  public AbstractDraweeController build() {
    ...
    return buildController();
  }

mDataSourceSupplier 創(chuàng)建賦值的流程大概出來(lái)了

SimpleDraweeView.setImageURI() --> PipelineDraweeControllerBuilder.build() --> PipelineDraweeControllerBuilder.buildController() --> PipelineDraweeControllerBuilder.obtainController() --> PipelineDraweeController.initialize() --> 賦值完成

按照上述流程繼續(xù)走下

mDataSourceSupplier 取值方法 obtainDataSourceSupplier(controller, controllerId)

protected Supplier<DataSource<IMAGE>> obtainDataSourceSupplier(
  final DraweeController controller, final String controllerId) {
...
// final image supplier;
if (mImageRequest != null) {
  supplier = getDataSourceSupplierForRequest(controller, controllerId, mImageRequest);
}

// 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(controller, controllerId, mLowResImageRequest));
  supplier = IncreasingQualityDataSourceSupplier.create(suppliers, false);
}
...
return supplier;
}

發(fā)現(xiàn)最終是走到getDataSourceSupplierForRequest方法。

   protected Supplier<DataSource<IMAGE>> getDataSourceSupplierForRequest(
       final DraweeController controller, String controllerId, REQUEST imageRequest) {
      return getDataSourceSupplierForRequest(
    controller, controllerId, imageRequest, CacheLevel.FULL_FETCH);
 }

 protected Supplier<DataSource<IMAGE>> getDataSourceSupplierForRequest(
  final DraweeController controller,
  final String controllerId,
  final REQUEST imageRequest,
    final CacheLevel cacheLevel) {
   final Object callerContext = getCallerContext();
     return new Supplier<DataSource<IMAGE>>() {
  @Override
  public DataSource<IMAGE> get() {
    // 這里就是 getDataSource() 時(shí)候真正調(diào)用的方法  
    return getDataSourceForRequest(
        controller, controllerId, imageRequest, callerContext, cacheLevel);
  }

  @Override
  public String toString() {
    ...
  }
};
 }

現(xiàn)在流程最終到getDataSourceForRequest()

2.5getDataSourceForRequest

現(xiàn)在流程為
onAttach() --> submitRequest() --> getDataSource() -->mDataSourceSupplier.get() --> getDataSourceForRequest()

   // PipelineDraweeControllerBuilder.java

     protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
  DraweeController controller,
  String controllerId,
  ImageRequest imageRequest,
  Object callerContext,
  AbstractDraweeControllerBuilder.CacheLevel cacheLevel) {

    return mImagePipeline.fetchDecodedImage(
    imageRequest,
    callerContext,
    convertCacheLevelToRequestLevel(cacheLevel),
    getRequestListener(controller));

}
可以看出來(lái) 最終把任務(wù)提交到mImagePipeline.fetchDecodedImage()去執(zhí)行。

2.6ImagePipeline是什么

ImagePipeline負(fù)責(zé)完成加載圖像,變成Android設(shè)備可呈現(xiàn)的形式所要做的每個(gè)事情。
pipelineSequence
大致流程如下:
1.檢查內(nèi)存緩存,如有,返回
2.后臺(tái)線(xiàn)程開(kāi)始后續(xù)工作
3.檢查是否在未解碼內(nèi)存緩存中。如有,解碼,變換,返回,然后緩存到內(nèi)存緩存中。
4.檢查是否在磁盤(pán)緩存中,如果有,變換,返回。緩存到未解碼緩存和內(nèi)存緩存中。
5.從網(wǎng)絡(luò)或者本地加載。加載完成后,解碼,變換,返回。存到各個(gè)緩存中。

至此流程基本結(jié)束

三、其他

3.1

Asynctask配合使用時(shí)候,注意阻塞問(wèn)題,Android4.0以后Asynctask就改成(先進(jìn)先出)誰(shuí)先來(lái)誰(shuí)先執(zhí)行并且只能一個(gè)線(xiàn)程執(zhí)行,這樣就導(dǎo)致了所有Http請(qǐng)求都阻塞了。

3.2從緩存中讀取文件

使用Fresco加載圖片,預(yù)覽保存圖片不在下載而是直接從緩存中讀取,

FileBinaryResource resource = (FileBinaryResource) Fresco.getImagePipelineFactory().getMainFileCache().getResource(new SimpleCacheKey(URI));
File file2 = resource.getFile();

說(shuō)明:
1.Fresco不同版本略不同,老版本是getMainFileCache()。
2.得到文件路徑是安裝包路徑,并且是.cnt未結(jié)尾。

/data/user/0/com.app.example/cache/image_cache/v2.ols100.1/67/MuA4Ubc_k3r3KFyVmpyoOw6RHU8.cnt

3.所以如果涉及到微信分享出去等,需要重新復(fù)制一個(gè).jpg或png的文件。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 本文適用網(wǎng)絡(luò)僅為“高校校園網(wǎng)”,目前運(yùn)營(yíng)商未商用,暫不適用;2. Win 10 ipv6存在問(wèn)題的根本原因是...
    Micro井閱讀 152,113評(píng)論 9 20
  • 個(gè)人學(xué)習(xí)批處理的初衷來(lái)源于實(shí)際工作;在某個(gè)迭代版本有個(gè)BS(安卓手游模擬器)大需求,從而在測(cè)試過(guò)程中就重復(fù)涉及到...
    Luckykailiu閱讀 4,981評(píng)論 0 11
  • NAME dnsmasq - A lightweight DHCP and caching DNS server....
    ximitc閱讀 2,991評(píng)論 0 0
  • 首先用這種方法不是所有人都可以你可能必須要有<公網(wǎng)IP>才可以【電信寬帶用戶(hù)打電話(huà)180區(qū)號(hào)0000免費(fèi)變更】 判...
    仁二閱讀 37,050評(píng)論 0 5
  • 今天幾件事 總算解決了三年級(jí)兩個(gè)班級(jí)缺數(shù)學(xué)老師的難題,同時(shí)二年級(jí)一個(gè)班缺數(shù)學(xué)老師的問(wèn)題也得了很好的處理,不過(guò)二年級(jí)...
    甲午之印閱讀 198評(píng)論 0 0

友情鏈接更多精彩內(nèi)容