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 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 dataSubscriber =
        new BaseDataSubscriber() {
          @Override
          public void onNewResultImpl(DataSource 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 dataSource) {
            onFailureInternal(id, dataSource, dataSource.getFailureCause(), /* isFinished */ true);
          }
          @Override
          public void onProgressUpdate(DataSource dataSource) {
            boolean isFinished = dataSource.isFinished();
            float progress = dataSource.getProgress();
            onProgressUpdateInternal(id, dataSource, progress, isFinished);
          }
        };
     //为DataSource增加subscriber
     mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
  }

这里可以看到我们的图片资源获取是通过getDatasource()方法来进行的,看下代码:

  @Override
  protected DataSource> 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 get() {
    return getDataSourceForRequest(imageRequest, callerContext, cacheLevel);
}

看下getDataSourceForRequest方法:

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

所以...我们终于进入到了ImagePipeline里面了...

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

首先是将管道搞定了,这个在上一篇中讲到过,然后就是submitFetchRequest方法:

  private  DataSource> submitFetchRequest(
      Producer> 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  DataSource> create(
      Producer> producer,
      SettableProducerContext settableProducerContext,
      RequestListener listener) {
    return new CloseableProducerToDataSourceAdapter(
        producer, settableProducerContext, listener);
  }

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

看下它的父类AbstractProducerToDataSourceAdapter是怎么初始化的:

  protected AbstractProducerToDataSourceAdapter(
      Producer 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 implements DataSource {
  
  ...
  //存储所有的subscriber
  private final ConcurrentLinkedQueue, Executor>> mSubscribers;

  protected AbstractDataSource() {
    mIsClosed = false;
    mDataSourceStatus = DataSourceStatus.IN_PROGRESS;
    mSubscribers = new ConcurrentLinkedQueue, 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 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, Executor> pair : mSubscribers) {
      notifyDataSubscriber(pair.first, pair.second, isFailure, isCancellation);
    }
  }
  
  //通知某一个Subscriber
  private void notifyDataSubscriber(
      final DataSubscriber 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, Executor> pair : mSubscribers) {
      final DataSubscriber 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 implements DataSubscriber {

  @Override
  public void onNewResult(DataSource dataSource) {
    final boolean shouldClose = dataSource.isFinished();
    try {
      onNewResultImpl(dataSource);
    } finally {
      //保证source最后会被关闭,防止内存泄漏
      if (shouldClose) {
        dataSource.close();
      }
    }
  }

  @Override
  public void onFailure(DataSource dataSource) {
    try {
      onFailureImpl(dataSource);
    } finally {
      //防止内存泄漏
      dataSource.close();
    }
  }

  @Override
  public void onCancellation(DataSource dataSource) {
  }

  @Override
  public void onProgressUpdate(DataSource dataSource) {
  }

  protected abstract void onNewResultImpl(DataSource dataSource);

  protected abstract void onFailureImpl(DataSource 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> 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> dataSource) {
         Log.d(TAG, "onFailureImpl: ");
    }
//构造一个单线程的Executor
}, Executors.newSingleThreadExecutor());

在这里因为我们是在子线程中执行Subscriber的方法,所以我们要进行UI操作的话需要传到UI线程中。

关于ImagePipeline的大致就是这样啦~现在真正实践的还不多,等以后用到的时候需要补充的再回来补吧。

你可能感兴趣的:(Android知识总结)