1. 程式人生 > >Android之Fresco框架(四)--ImagePipeline的呼叫和使用

Android之Fresco框架(四)--ImagePipeline的呼叫和使用

之前大致把ImagePipeline的配置和底層實現都講了一下,這一篇來重點講一下我們在傳送圖片請求的時候是怎麼把請求傳給ImagePipeline的,以及我們如何自己直接對ImagePipeline例項進行請求,記憶體管理等操作。

SimpleDraweeView中ImagePipeline的呼叫

在第一篇的時候,我們當時只需要對SimpleDraweeView進行setImageURI()方法,setController()方法之後,都能夠成功發起圖片請求,那底層是怎麼實現的呢。先來看一下SimpleDraweeView中的相關程式碼:

/**
   * Displays an image given by the uri.
   *
   * @param uri uri of the image
   * @undeprecate
   */
  @Override
  public void setImageURI(Uri uri) {
    setImageURI(uri, null);
  }

  /**
   * Displays an image given by the uri string.
   *
   * @param uriString uri string of the image
   */
  public void setImageURI(@Nullable String uriString) {
    setImageURI(uriString, null);
  }

  /**
   * 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();
    //所有setImageURI最終呼叫方法
    setController(controller);
  }

  /**
   * Displays an image given by the uri string.
   *
   * @param uriString uri string of the image
   * @param callerContext caller context
   */
  public void setImageURI(@Nullable String uriString, @Nullable Object callerContext) {
    Uri uri = (uriString != null) ? Uri.parse(uriString) : null;
    setImageURI(uri, callerContext);
  }

可以看出,不管用的是哪一個過載的setImageURI,最終都是會呼叫到同一個方法中,而該方法中又會執行setController()方法。那我們來看一下setController()方法,所以我們用不管setImageURI()還是setController()最終都是殊途同歸。setController()在父類DraweeView()中。DraweeView是一個繼承於ImageView的子類:

private DraweeHolder<DH> mDraweeHolder;
public void setController(@Nullable DraweeController draweeController) {
    mDraweeHolder.setController(draweeController);
    super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
}

所以方法又呼叫了DraweeHolder裡面的setController()方法,對於DraweeHolder,個人感覺是起到了介面卡的作用,也使得我們的程式碼更加易讀吧。先不瞎扯看下DraweeHolder中的程式碼:

  public void setController(@Nullable DraweeController draweeController) {
    boolean wasAttached = mIsControllerAttached;
    if (wasAttached) {
      detachController();
    }

    //清除掉舊的Controller
    if (isControllerValid()) {
      //mEventTracker起到一個紀錄的作用
      mEventTracker.recordEvent(Event.ON_CLEAR_OLD_CONTROLLER);
      mController.setHierarchy(null);
    }
    mController = draweeController;
    if (mController != null) {
      mEventTracker.recordEvent(Event.ON_SET_CONTROLLER);
      mController.setHierarchy(mHierarchy);
    } else {
      mEventTracker.recordEvent(Event.ON_CLEAR_CONTROLLER);
    }

    if (wasAttached) {
      //呼叫到該方法
      attachController();
    }
  }


  private void attachController() {
    if (mIsControllerAttached) {
      return;
    }
    mEventTracker.recordEvent(Event.ON_ATTACH_CONTROLLER);
    mIsControllerAttached = true;
    if (mController != null &&
        mController.getHierarchy() != null) {
          //呼叫Controller的onAttach()方法
          mController.onAttach();
    }
  }

接下來就是看Controller的onAttach方法,在SimpleDraweeView中設定的PipelineDraweeController例項,onAttach方法定義於它的父類AbstractDraweeController中:

  @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();
    }
  }

前面的都不重要,最主要是這裡又呼叫了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;
    //獲得圖片對應的DataSource
    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();
    //用一個subscriber來管理獲取的結果,保證DataSource最終會被關閉
    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);
          }
        };
     //為DataSource增加subscriber
     mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
  }

這裡可以看到我們的圖片資源獲取是通過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的get方法在AbstractDraweeControllerBuilder中的getDataSourceSupplierForRequest方法中被重寫      
@Override
public DataSource<IMAGE> get() {
    return getDataSourceForRequest(imageRequest, callerContext, cacheLevel);
}

看下getDataSourceForRequest方法:

  @Override
  protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
      ImageRequest imageRequest,
      Object callerContext,
      AbstractDraweeControllerBuilder.CacheLevel cacheLevel) {
    //接下來要進入的方法    
    return mImagePipeline.fetchDecodedImage(
        imageRequest,
        callerContext,
        convertCacheLevelToRequestLevel(cacheLevel));
  }

所以...我們終於進入到了ImagePipeline裡面了...

  public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(
      ImageRequest imageRequest,
      Object callerContext,
      ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) {
    try {
      //獲得Producer管道
      Producer<CloseableReference<CloseableImage>> producerSequence =
          mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
      //接下來要進入的方法
      return submitFetchRequest(
          producerSequence,
          imageRequest,
          lowestPermittedRequestLevelOnSubmit,
          callerContext);
    } catch (Exception exception) {
      return DataSources.immediateFailedDataSource(exception);
    }
  }

首先是將管道搞定了,這個在上一篇中講到過,然後就是submitFetchRequest方法:

  private <T> DataSource<CloseableReference<T>> submitFetchRequest(
      Producer<CloseableReference<T>> producerSequence,
      ImageRequest imageRequest,
      ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit,
      Object callerContext) {
    final RequestListener requestListener = getRequestListenerForRequest(imageRequest);

    try {
      //各種設定
      ImageRequest.RequestLevel lowestPermittedRequestLevel =
          ImageRequest.RequestLevel.getMax(
              imageRequest.getLowestPermittedRequestLevel(),
              lowestPermittedRequestLevelOnSubmit);
      SettableProducerContext settableProducerContext = new SettableProducerContext(
          imageRequest,
          generateUniqueFutureId(),
          requestListener,
          callerContext,
          lowestPermittedRequestLevel,
        /* isPrefetch */ false,
          imageRequest.getProgressiveRenderingEnabled() ||
              imageRequest.getMediaVariations() != null ||
              !UriUtil.isNetworkUri(imageRequest.getSourceUri()),
          imageRequest.getPriority());
      //接下來要進入的方法
      return CloseableProducerToDataSourceAdapter.create(
          producerSequence,
          settableProducerContext,
          requestListener);
    } catch (Exception exception) {
      return DataSources.immediateFailedDataSource(exception);
    }
  }

前面一堆就不說了直接看CloseableProducerToDataSourceAdapter.create方法:

  public static <T> DataSource<CloseableReference<T>> create(
      Producer<CloseableReference<T>> producer,
      SettableProducerContext settableProducerContext,
      RequestListener listener) {
    return new CloseableProducerToDataSourceAdapter<T>(
        producer, settableProducerContext, listener);
  }

  private CloseableProducerToDataSourceAdapter(
      Producer<CloseableReference<T>> producer,
      SettableProducerContext settableProducerContext,
      RequestListener listener) {
    super(producer, settableProducerContext, listener);
  }

看下它的父類AbstractProducerToDataSourceAdapter是怎麼初始化的:

  protected AbstractProducerToDataSourceAdapter(
      Producer<T> producer,
      SettableProducerContext settableProducerContext,
      RequestListener requestListener) {
    mSettableProducerContext = settableProducerContext;
    mRequestListener = requestListener;
    mRequestListener.onRequestStart(
        settableProducerContext.getImageRequest(),
        mSettableProducerContext.getCallerContext(),
        mSettableProducerContext.getId(),
        mSettableProducerContext.isPrefetch());
    //管道中的producer開始呼叫produceResults執行請求了
    producer.produceResults(createConsumer(), settableProducerContext);
  }

當看到最後一行的時候,終於看到了結局,producer.produceResults()上一篇已經說過了,就是pipeline中的producer開始執行自己的工作了。所以到這裡,我們的整個請求也就正式跟上面的內容接軌了。實在是...很曲折...

這個是我們通過呼叫DraweeView來呼叫到ImagePipeline發起圖片請求的,我們也可以直接獲得Fresco中的ImagePipeline,直接傳入我們的ImageRequest來實現請求操作。

在看ImagePipeline之前先看一下幾個其他的類:

DataSource與DataSubscriber

DataSource跟Future相似,都會儲存我們的非同步計算的結果,不過不同的是,DataSource會返回一系列的結果。DataSubscriber是一個用於對DataSource進行監聽的類,當DataSource中的資料處理有了結果,不管是成功還是失敗,都會向Subscriber發出通知,讓Subscriber進行相應的處理,這類似於設計模式中的觀察者模式。

我們看一下AbstractDataSource中的程式碼:


public abstract class AbstractDataSource<T> implements DataSource<T> {
  
  ...
  //儲存所有的subscriber
  private final ConcurrentLinkedQueue<Pair<DataSubscriber<T>, Executor>> mSubscribers;

  protected AbstractDataSource() {
    mIsClosed = false;
    mDataSourceStatus = DataSourceStatus.IN_PROGRESS;
    mSubscribers = new ConcurrentLinkedQueue<Pair<DataSubscriber<T>, Executor>>();
  }

  ...

  @Override
  public boolean close() {
    T resultToClose;
    synchronized (this) {
      if (mIsClosed) {
        return false;
      }
      mIsClosed = true;
      resultToClose = mResult;
      mResult = null;
    }
    if (resultToClose != null) {
      closeResult(resultToClose);
    }
    if (!isFinished()) {
      notifyDataSubscribers();
    }
    synchronized (this) {
      mSubscribers.clear();
    }
    return true;
  }

  ...

  //新增Subscriber以及執行Subscriber對應的executor
  @Override
  public void subscribe(final DataSubscriber<T> dataSubscriber, final Executor executor) {
    Preconditions.checkNotNull(dataSubscriber);
    Preconditions.checkNotNull(executor);
    boolean shouldNotify;

    synchronized(this) {
      if (mIsClosed) {
        return;
      }

      if (mDataSourceStatus == DataSourceStatus.IN_PROGRESS) {
        mSubscribers.add(Pair.create(dataSubscriber, executor));
      }
      
      //當這三個狀態有一個為true時,需要同時Subscriber進行處理
      shouldNotify = hasResult() || isFinished() || wasCancelled();
    }

    if (shouldNotify) {
      notifyDataSubscriber(dataSubscriber, executor, hasFailed(), wasCancelled());
    }
  }

  //通知Subscriber
  private void notifyDataSubscribers() {
    final boolean isFailure = hasFailed();
    final boolean isCancellation = wasCancelled();
    for (Pair<DataSubscriber<T>, Executor> pair : mSubscribers) {
      notifyDataSubscriber(pair.first, pair.second, isFailure, isCancellation);
    }
  }
  
  //通知某一個Subscriber
  private void notifyDataSubscriber(
      final DataSubscriber<T> dataSubscriber,
      final Executor executor,
      final boolean isFailure,
      final boolean isCancellation) {
    executor.execute(
        new Runnable() {
          @Override
          public void run() {
            if (isFailure) {
              dataSubscriber.onFailure(AbstractDataSource.this);
            } else if (isCancellation) {
              dataSubscriber.onCancellation(AbstractDataSource.this);
            } else {
              dataSubscriber.onNewResult(AbstractDataSource.this);
            }
          }
        });
  }

  ...

  //通知Subscriber下載程序有更新
  protected void notifyProgressUpdate() {
    for (Pair<DataSubscriber<T>, Executor> pair : mSubscribers) {
      final DataSubscriber<T> subscriber = pair.first;
      Executor executor = pair.second;
      executor.execute(
          new Runnable() {
            @Override
            public void run() {
              subscriber.onProgressUpdate(AbstractDataSource.this);
            }
          });
    }
  }
}

這裡就不將所有的程式碼,主要是看一下DataSource與Subscriber之間的互動部分。在訂閱的時候,需要傳入一個Executor,這是為了讓Subscriber在該Executor上執行我們的操作,這也方便我們進行定製,例如要直接在主執行緒對source進行處理或者是先放在其他子執行緒進行一些耗時的操作。其他的方法主要是涉及到一些下載狀態量的setter和getter。再看一下BaseDataSubscriber的程式碼:

public abstract class BaseDataSubscriber<T> implements DataSubscriber<T> {

  @Override
  public void onNewResult(DataSource<T> dataSource) {
    final boolean shouldClose = dataSource.isFinished();
    try {
      onNewResultImpl(dataSource);
    } finally {
      //保證source最後會被關閉,防止記憶體洩漏
      if (shouldClose) {
        dataSource.close();
      }
    }
  }

  @Override
  public void onFailure(DataSource<T> dataSource) {
    try {
      onFailureImpl(dataSource);
    } finally {
      //防止記憶體洩漏
      dataSource.close();
    }
  }

  @Override
  public void onCancellation(DataSource<T> dataSource) {
  }

  @Override
  public void onProgressUpdate(DataSource<T> dataSource) {
  }

  protected abstract void onNewResultImpl(DataSource<T> dataSource);

  protected abstract void onFailureImpl(DataSource<T> dataSource);
}

從這個程式碼可以看到,Subscriber中再onNewResult和onFailure中對保證了記憶體不會洩漏,而我們一般在重寫BaseDataSource的時候一般只需要重寫onNewResultImpl和onFailureImpl,這樣能夠保證安全。不過有一個點就是,dataSource在onNewResult或onFailure方法執行結束之後就會被close(),所以我們如果要在方法外面用到時,需要對資料進行復制。

ImagePipeline

之前我們在講程式碼的時候已經提到了ImagePipeline中的fetchDecodedImage方法和submitFetchRequest方法的程式碼了。除了這些之外,還有其他一些方法。

例如針對影象請求的,有:

fetchEncodedImage:獲取到未解碼的資料

fetchImageFromBitmapCache:只能從MemoryCache中獲取資料

這些都是構造出對應的ProduceSequence,然後由submitFetchRequest方法來發出請求操作。

針對影象預快取的,有:

prefetchToBitmapCache:將資料先快取Cache中

prefetchToDiskCache:將資料先快取到Disk中

這些也是構造出對應的ProduceSequence,然後由submitPrefetchRequest方法來發出請求操作。

針對記憶體管理的,有:

evictFromMemoryCache:將某個uri對應的資料從MemoryCache中刪除掉

evictFromDiskCache:將某個uri或ImageRequest對應的資料從Disk中刪除掉

evictFromCache:將某個uri對應的資料從所有快取中刪除掉

clearMemoryCaches:清除MemoryCache中的內容

clearDiskCaches:清除Disk中的內容

clearCaches:清除所有快取中的內容

以上就是一些我們主要會用到的方法了。接下來看一下我們怎麼直接使用ImagePipeline進行影象請求。

自定義ImageRequest

//構造ImageRequest
private ImageRequest getImageRequest() {
        //後處理器
        Postprocessor processor = new MyPostprocessor();
        ImageRequest request = ImageRequestBuilder
                //設定URI
                .newBuilderWithSource(uri)
                //自動旋轉
                .setAutoRotateEnabled(true)
                //最低級別請求
                .setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
                .setProgressiveRenderingEnabled(false)
                .setPostprocessor(processor)
                .build();
        return request;
    }

獲取Fresco的ImagePipeline併發從ImageRequest:

//獲取ImagePipeline
ImagePipeline imagePipeline = Fresco.getImagePipeline();
ImageRequest request = getImageRequest();
//傳送請求,獲取已經解碼的圖片資料
DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(request,this);
//這裡是繼承了BaseBitmapDataSubscriber,會將CloseableImage轉為CloseableBitmap
dataSource.subscribe(new BaseBitmapDataSubscriber() {

    @Override
    protected void onNewResultImpl(Bitmap bitmap) {
        //複製資料
        Bitmap newBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        //利用Handler傳到UI執行緒中
        Message message = new Message();
        message.what = 0;
        message.obj = newBitmap;
        handler.sendMessage(message);
        Log.d(TAG, "onNewResultImpl: ");
    }

    @Override
    protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
         Log.d(TAG, "onFailureImpl: ");
    }
//構造一個單執行緒的Executor
}, Executors.newSingleThreadExecutor());

在這裡因為我們是在子執行緒中執行Subscriber的方法,所以我們要進行UI操作的話需要傳到UI執行緒中。

關於ImagePipeline的大致就是這樣啦~現在真正實踐的還不多,等以後用到的時候需要補充的再回來補吧。