Fresco源码分析-图片加载初探

作者: susion哒哒 | 来源:发表于2018-04-10 09:08 被阅读46次

    在使用Fresco时,最常用的莫过于这个方法:void setImageURI(@Nullable String uriString)。那么就以这个方法为起点,追踪fresco的图片加载流程。

    setImageURI()

    SimpleDraweeView.java

      public void setImageURIroller = mSimpleDraweeControllerBuilder
            .setCallerContext(callerContext)
            .setUri(uri)
            .setOldController(getController())
            .build();
        setController(controller);
      }
    

    可以看到这个方法就设置了一个DraweeController。其实就是一个PipelineDraweeController对象。这个对象持有要加载图片的Uri,那么看来对于图片的加载PipelineDraweeController是关键。

    继续追踪 setController -> DraweeView.java

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

    即实际上是把Controller设置给了DraweeHolder。并立刻显示DraweeHolder中的最顶层的图片。

    第一章已经知道Fresco在加载图片时,对于不同的阶段会显示不同的图片,具体使用一个DraweeHierarchy的概念来组织。这里其实可以猜出mDraweeHolder.getTopLevelDrawable()就是拿出DraweeHierarchy中我们设置的 placeholder 图片。

    这段代码把要做的事情都委托给了DraweeHolder。在看具体下面流程之前,先大致看一下DraweeHolder是干什么的。

    DraweeHolder

    /**
     * A holder class for Drawee controller and hierarchy.
     *
     * <p>Drawee users, should, as a rule, use {@link DraweeView} or its subclasses. There are
     * situations where custom views are required, however, and this class is for those circumstances.
     *
     * <p>Each {@link DraweeHierarchy} object should be contained in a single instance of this
     * class.
       ...
     */
    public class DraweeHolder<DH extends DraweeHierarchy> implements VisibilityCallback 
    

    即这个类看样是维护 Drawee controller 和 hierarchy关系的。DraweeHierarchyDraweeHolder是一对一的关系。

    接下来继续追踪 setController -> DraweeHolder

      public void setController(@Nullable DraweeController draweeController) {
        boolean wasAttached = mIsControllerAttached;
        if (wasAttached) {
          detachController();
        }
    
        // Clear the old controller
        if (isControllerValid()) {
          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();
        }
      }
    

    上面代码维护了Hierarchy与Controller的关系,一个Hierarchy被一个Controller引用。把Controller操作相关的一些事件记录到DraweeEventTracker。然后调用attachController()

    DraweeEventTracker是一个This class keeps a record of internal events that take place in the Drawee。他主要用来调试。

    attachController()最终会调用 mController.onAttach(),在这个方法中发起了图片加载事件。那么来看一下onAttach() -> AbstractDraweeController.java

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

    可以看到这个方法主要记录了一些状态,并且要mSettableDraweeHierarchy不为null,这里其实就有点问题了,我们其实并没有在加载图片时设置过DraweeHierarchy那这个是哪里来的呢?其实我们在布局文件,或者手动new SimpleDraweeView时都会触发GenericDraweeViewinflateHierarchy。触发时机当然是在设置DraweeHierarchy时。

      protected void inflateHierarchy(Context context, @Nullable AttributeSet attrs) {
        GenericDraweeHierarchyBuilder builder =
            GenericDraweeHierarchyInflater.inflateBuilder(context, attrs);
        setAspectRatio(builder.getDesiredAspectRatio());
        setHierarchy(builder.build());
      }
    

    mSettableDraweeHierarchy其实就是一个GenericDraweeHierarchy对象。在判断这个对象不为null后,Controller调用了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);
          onImageLoadedFromCacheImmediately(mId, closeableImage);
          onNewResultInternal(mId, mDataSource, closeableImage, 1.0f, true, true);
          return;
        }
        ......
        final String id = mId;
        final boolean wasImmediate = mDataSource.hasResult();
        final DataSubscriber<T> dataSubscriber =
            new BaseDataSubscriber<T>() {
              @Override
              public void onNewResultImpl(DataSource<T> dataSource) {
                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);
              }
            };
        mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
      }
    

    大体上逻辑是这样的:在有缓存的情况下直接使用缓存中的图片,在没有缓存时,使用DataSource加载图片。

    看一下是如何从内存中获取缓存图片的:PipelineDraweeController -> getCachedImage():

     protected CloseableReference<CloseableImage> getCachedImage() {
        if (mMemoryCache == null || mCacheKey == null) {
          return null;
        }
        // We get the CacheKey
        CloseableReference<CloseableImage> closeableImage = mMemoryCache.get(mCacheKey);
        if (closeableImage != null && !closeableImage.get().getQualityInfo().isOfFullQuality()) {
          closeableImage.close();
          return null;
        }
        return closeableImage;
      }
    

    CloseableReference实际上是一个持有引用计数的指针,当他所引用的对象引用数量为0,调用close()

    private @Nullable MemoryCache<CacheKey, CloseableImage> mMemoryCache

    那么如何使用DataSource加载图片的呢?

      final boolean wasImmediate = mDataSource.hasResult();
    

    DataSource

    跟着mDataSource.hasResult(),追踪一下mDataSource是哪个类的实例,如何加载数据。

    dataSoure通过 AbstractDraweeController -> obtainDataSourceSupplier()方法获得。

      /** Gets the top-level data source supplier to be used by a controller. */
      protected Supplier<DataSource<IMAGE>> obtainDataSourceSupplier() {
        //....
        // final image supplier;
        if (mImageRequest != null) {
          supplier = getDataSourceSupplierForRequest(mImageRequest);
        ....
        return supplier;
      }
    

    可以看到应根据不同的image request获得不同的datasource。追踪源码,最终的data soure是在ImagePipelinefetchDecodedImage()方法获得。

      public DataSource<CloseableReference<CloseableImage>> fetchDecodedImage(
          ImageRequest imageRequest,
          Object callerContext,
          ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit) {
        try {
          Producer<CloseableReference<CloseableImage>> producerSequence =
              mProducerSequenceFactory.getDecodedImageProducerSequence(imageRequest);
          return submitFetchRequest(
              producerSequence,
              imageRequest,
              lowestPermittedRequestLevelOnSubmit,
              callerContext);
        } catch (Exception exception) {
          return DataSources.immediateFailedDataSource(exception);
        }
      }
    
      private <T> DataSource<CloseableReference<T>> submitFetchRequest(
          Producer<CloseableReference<T>> producerSequence,
          ImageRequest imageRequest,
          ImageRequest.RequestLevel lowestPermittedRequestLevelOnSubmit,
          Object callerContext) {
          ......
          return CloseableProducerToDataSourceAdapter.create(
              producerSequence,
              settableProducerContext,
              requestListener);
        } catch (Exception exception) {
          return DataSources.immediateFailedDataSource(exception);
        }
      }
      ...
    

    可以看到这里DataSource返回的是CloseableProducerToDataSourceAdapter的实例。那么数据源到底如何发起图片加载请求的呢?看一下CloseableProducerToDataSourceAdapter构造函数:

      protected AbstractProducerToDataSourceAdapter(
          Producer<T> producer,
          SettableProducerContext settableProducerContext,
          RequestListener requestListener) {
          //....
         producer.produceResults(createConsumer(), settableProducerContext);
      }
    

    这里其实就是调用producerproduceResults()来产生请求结果,并通过回调来设置返回结果。而上面的mDataSource.hasResult()就是判断当前有没有返回结果。

    通过上面的源码分析,这里已经知道,DataSource并不会去加载图片,它会把图片加载的细节委托给Producer,它只是负责发起Producer的图片加载请求,并监听返回结果给外部。

    使用下面这张图总结一下流程:

    progress.png

    相关文章

      网友评论

        本文标题:Fresco源码分析-图片加载初探

        本文链接:https://www.haomeiwen.com/subject/mkkghftx.html