Fresco:SimpleDraweeView如何显示并加载图片

使用Fresco, 如果仅仅是想简单下载一张网络图片,在下载完成之前,显示一张占位图,那么简单使用 SimpleDraweeView 即可。那么SimpleDraweeView是如何如何显示并加载图片呢?

我们知道在使用Fresco前,需要调用

Fresco.initialize(context);

Fresco.initialize做哪些工作

该函数有两个实现:

/** Initializes Fresco with the default config. */
public static void initialize(Context context) {
  ImagePipelineFactory.initialize(context);
  initializeDrawee(context);
}

/** Initializes Fresco with the specified config. */
public static void initialize(Context context, ImagePipelineConfig imagePipelineConfig) {
  ImagePipelineFactory.initialize(imagePipelineConfig);
  initializeDrawee(context);
}

第一个无需配置ImagePipelineConfig,这种情况下使用默认配置。会构造一个只包含mContext值的ImagePipelineConfig对象,该对象中其他成员变量为空。

该函数主要做两件事,第一初始化ImagePipelineFactory,第二初始化Drawee:将使用配置的ImagePipelineConfig(默认的,或使用自定义参数的)构造的ImagePipeline赋值给Fresco的静态成员变量sDraweeControllerBuilderSupplier 的成员变量mImagePipeline.

private static void initializeDrawee(Context context) {
  sDraweeControllerBuilderSupplier = new PipelineDraweeControllerBuilderSupplier(context);
  SimpleDraweeView.initialize(sDraweeControllerBuilderSupplier);
}

然后使用sDraweeControllerBuilderSupplier初始化SimpleDraweeView。sDraweeControllerBuilderSupplier的类型为:
public class PipelineDraweeControllerBuilderSupplier implements
Supplier

SimpleDraweeView的构造函数

SimpleDraweeView的构造函数会调用init()函数,该函数会初始化成员变量mSimpleDraweeControllerBuilder ,类型为:SimpleDraweeControllerBuilder。

private void init() {
  if (isInEditMode()) {
    return;
  }
  Preconditions.checkNotNull(
      sDraweeControllerBuilderSupplier,
      "SimpleDraweeView was not initialized!");
  mSimpleDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get();
}

sDraweeControllerBuilderSupplier.get()返回PipelineDraweeControllerBuilder类型的数据,该类继承AbstractDraweeControllerBuilder类,后者继承了SimpleDraweeControllerBuilder接口。

@Override
public PipelineDraweeControllerBuilder get() {
  return new PipelineDraweeControllerBuilder(
      mContext,
      mPipelineDraweeControllerFactory,
      mImagePipeline,
      mBoundControllerListeners);
}

SimpleDraweeView::setImageURI()

我们通过setImageURI为调用SimpleDraweeView设置图片,
该函数分两部分,第一创建DraweeController ,第二设置Controller.

创建DraweeController

通过调用mSimpleDraweeControllerBuilder的build()函数实现,代码如下:

/**
 * 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();
  setController(controller);
}

即调用drawee\src\main\java\com\facebook\drawee\controller\AbstractDraweeControllerBuilder.java 的build():

/** Builds the specified controller. */
@Override
public AbstractDraweeController build() {
  validate();

  // if only a low-res request is specified, treat it as a final request.
  if (mImageRequest == null && mMultiImageRequests == null && mLowResImageRequest != null) {
    mImageRequest = mLowResImageRequest;
    mLowResImageRequest = null;
  }

  return buildController();
}
/** Builds a regular controller. */
protected AbstractDraweeController buildController() {
  AbstractDraweeController controller = obtainController();
  controller.setRetainImageOnFailure(getRetainImageOnFailure());
  maybeBuildAndSetRetryManager(controller);
  maybeAttachListeners(controller);
  return controller;
}

obtainController()的实现在PipelineDraweeControllerBuilder.java中:

@Override
protected PipelineDraweeController obtainController() {
  DraweeController oldController = getOldController();
  PipelineDraweeController controller;
  if (oldController instanceof PipelineDraweeController) {
    controller = (PipelineDraweeController) oldController;
    controller.initialize(
        obtainDataSourceSupplier(),
        generateUniqueControllerId(),
        getCallerContext());
  } else {
    controller = mPipelineDraweeControllerFactory.newController(
        obtainDataSourceSupplier(),
        generateUniqueControllerId(),
        getCallerContext());
  }
  return controller;
}

调用AbstractDraweeControllerBuilder.java的obtainDataSourceSupplier():

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

  Supplier<DataSource<IMAGE>> supplier = null;

  // final image supplier;
  if (mImageRequest != null) {
    supplier = getDataSourceSupplierForRequest(mImageRequest);
  } else if (mMultiImageRequests != null) {
    supplier = getFirstAvailableDataSourceSupplier(mMultiImageRequests, mTryCacheOnlyFirst);
  }

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

  // no image requests; use null data source supplier
  if (supplier == null) {
    supplier = DataSources.getFailedDataSourceSupplier(NO_REQUEST_EXCEPTION);
  }

  return supplier;
}
/** Creates a data source supplier for the given image request. */
protected Supplier<DataSource<IMAGE>> getDataSourceSupplierForRequest(
    final REQUEST imageRequest,
    final boolean bitmapCacheOnly) {
  final Object callerContext = getCallerContext();
  return new Supplier<DataSource<IMAGE>>() {
    @Override
    public DataSource<IMAGE> get() {
      return getDataSourceForRequest(imageRequest, callerContext, bitmapCacheOnly);
    }
    @Override
    public String toString() {
      return Objects.toStringHelper(this)
          .add("request", imageRequest.toString())
          .toString();
    }
  };
}

最后通过PipelineDraweeControllerBuilder.java的getDataSourceForRequest()返回DataSourceSupplier。该DataSourceSupplier的get()会调用getDataSourceForRequest()将获取到的图片数据返回,返回数据类型为:DataSource

@Override
protected DataSource<CloseableReference<CloseableImage>> getDataSourceForRequest(
    ImageRequest imageRequest,
    Object callerContext,
    boolean bitmapCacheOnly) {
  if (bitmapCacheOnly) {
    return mImagePipeline.fetchImageFromBitmapCache(imageRequest, callerContext);
  } else {
    return mImagePipeline.fetchDecodedImage(imageRequest, callerContext);
  }
}
设置Controller

接着回到 setImageURI函数中的 setController(controller)的实现:

  /** Sets the controller. */
  public void setController(@Nullable DraweeController draweeController) {
    mDraweeHolder.setController(draweeController);
    super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
  }

调用DraweeHolder.java的setController()函数:

 /**
   * Sets a new controller.
   */
  public void setController(@Nullable DraweeController draweeController) {
    boolean wasAttached = mIsControllerAttached;
    if (wasAttached) {
      detachController();
    }

    // Clear the old controller
    if (mController != null) {
      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();
    }
  }

该函数主要的功能是调用mController.setHierarchy(mHierarchy);关于mHierarchy,是在SimpleDraweeView的父类GenericDraweeView的构造函数中设置的:

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

根据我们在XML中声明的SimpleDraweeView的一些属性创建GenericDraweeHierarchyBuilder,然后调用它的build()函数生成GenericDraweeHierarchy:

  /**
   * Inflates a new hierarchy builder from XML.
   * The builder can then be modified in order to override XML attributes if necessary.
   */
  public static GenericDraweeHierarchyBuilder inflateBuilder(
      Context context,
      @Nullable AttributeSet attrs) {
    Resources resources = context.getResources();
    GenericDraweeHierarchyBuilder builder = new GenericDraweeHierarchyBuilder(resources);
    return updateBuilder(builder, context, attrs);
  }
  /**
   * Updates the existing hierarchy builder based on the XML attributes.
   *
   * This method is useful if a custom view uses different default values. In that case a
   * builder with adjusted default values can be passed to this method and only the properties
   * explicitly specified in XML will be overridden.
   * The builder can be modified afterwards in case some XML attributes needs to be overridden.
   *
   * @param builder a hierarchy builder to be updated
   * @return the modified instance of the same builder
   */
  public static GenericDraweeHierarchyBuilder updateBuilder(
      GenericDraweeHierarchyBuilder builder,
      Context context,
      @Nullable AttributeSet attrs) {
    // these paramters cannot be applied immediately so we store them first
    int progressBarAutoRotateInterval = 0;
    int roundedCornerRadius = 0;
    boolean roundTopLeft = true;
    boolean roundTopRight = true;
    boolean roundBottomLeft = true;
    boolean roundBottomRight = true;

    if (attrs != null) {
      TypedArray gdhAttrs = context.obtainStyledAttributes(
        attrs,
        R.styleable.GenericDraweeHierarchy);
      try {
        final int indexCount = gdhAttrs.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
          final int attr = gdhAttrs.getIndex(i);
          // most popular ones first
          if (attr == R.styleable.GenericDraweeHierarchy_actualImageScaleType) {
            builder.setActualImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_placeholderImage) {
            builder.setPlaceholderImage(getDrawable(context, gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_pressedStateOverlayImage) {
            builder.setPressedStateOverlay(getDrawable(context, gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_progressBarImage) {
            builder.setProgressBarImage(getDrawable(context, gdhAttrs, attr));

          // the remaining ones without any particular order
          } else if (attr == R.styleable.GenericDraweeHierarchy_fadeDuration) {
            builder.setFadeDuration(gdhAttrs.getInt(attr, 0));

          } else if (attr == R.styleable.GenericDraweeHierarchy_viewAspectRatio) {
            builder.setDesiredAspectRatio(gdhAttrs.getFloat(attr, 0));

          } else if (attr == R.styleable.GenericDraweeHierarchy_placeholderImageScaleType) {
            builder.setPlaceholderImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_retryImage) {
            builder.setRetryImage(getDrawable(context, gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_retryImageScaleType) {
            builder.setRetryImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_failureImage) {
            builder.setFailureImage(getDrawable(context, gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_failureImageScaleType) {
            builder.setFailureImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_progressBarImageScaleType) {
            builder.setProgressBarImageScaleType(getScaleTypeFromXml(gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_progressBarAutoRotateInterval) {
            progressBarAutoRotateInterval =
                gdhAttrs.getInteger(attr, progressBarAutoRotateInterval);

          } else if (attr == R.styleable.GenericDraweeHierarchy_backgroundImage) {
            builder.setBackground(getDrawable(context, gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_overlayImage) {
            builder.setOverlay(getDrawable(context, gdhAttrs, attr));

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundAsCircle) {
            getRoundingParams(builder).setRoundAsCircle(gdhAttrs.getBoolean(attr, false));

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundedCornerRadius) {
            roundedCornerRadius = gdhAttrs.getDimensionPixelSize(attr, roundedCornerRadius);

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundTopLeft) {
            roundTopLeft = gdhAttrs.getBoolean(attr, roundTopLeft);

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundTopRight) {
            roundTopRight = gdhAttrs.getBoolean(attr, roundTopRight);

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundBottomLeft) {
            roundBottomLeft = gdhAttrs.getBoolean(attr, roundBottomLeft);

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundBottomRight) {
            roundBottomRight = gdhAttrs.getBoolean(attr, roundBottomRight);

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundWithOverlayColor) {
            getRoundingParams(builder).setOverlayColor(gdhAttrs.getColor(attr, 0));

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundingBorderWidth) {
            getRoundingParams(builder).setBorderWidth(gdhAttrs.getDimensionPixelSize(attr, 0));

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundingBorderColor) {
            getRoundingParams(builder).setBorderColor(gdhAttrs.getColor(attr, 0));

          } else if (attr == R.styleable.GenericDraweeHierarchy_roundingBorderPadding) {
            getRoundingParams(builder).setPadding(gdhAttrs.getDimensionPixelSize(attr, 0));

          }
        }
      } finally {
        gdhAttrs.recycle();
      }
    }
 // wrap progress bar if auto-rotating requested
    if (builder.getProgressBarImage() != null && progressBarAutoRotateInterval > 0) {
      builder.setProgressBarImage(
        new AutoRotateDrawable(builder.getProgressBarImage(), progressBarAutoRotateInterval));
    }

    // set rounded corner radii if requested
    if (roundedCornerRadius > 0) {
      getRoundingParams(builder).setCornersRadii(
        roundTopLeft ? roundedCornerRadius : 0,
        roundTopRight ? roundedCornerRadius : 0,
        roundBottomRight ? roundedCornerRadius : 0,
        roundBottomLeft ? roundedCornerRadius : 0);
    }

    return builder;
  }    

再返回setController(controller)中,可以看到调用 super.setImageDrawable(mDraweeHolder.getTopLevelDrawable()),即ImageView的setImageDrawable函数,让图片在SimpleDraweeView中显示。

GenericDraweeHierarchy

这是Fresco的特点,每个SimpleDraweeView设置的图片都是RootDrawable类型的,对应GenericDraweeHierarchy的mTopLevelDrawable成员变量,而GenericDraweeHierarchy是类似下图的hierachy:

   o RootDrawable (top level drawable)
   |
   +--o FadeDrawable
      |
      +--o ScaleTypeDrawable (placeholder branch, optional)
      |  |
      |  +--o Drawable (placeholder image)
      |
      +--o ScaleTypeDrawable (actual image branch)
      |  |
      |  +--o ForwardingDrawable (actual image wrapper)
      |     |
      |     +--o Drawable (actual image)
      |
      +--o null (progress bar branch, optional)
      |
      +--o Drawable (retry image branch, optional)
      |
      +--o ScaleTypeDrawable (failure image branch, optional)
         |
         +--o Drawable (failure image)

在该类的构造函数中我们可以看到对上述各层图片的创建过程:

GenericDraweeHierarchy(GenericDraweeHierarchyBuilder builder) {
    mResources = builder.getResources();
    mRoundingParams = builder.getRoundingParams();

    mActualImageWrapper = new ForwardingDrawable(mEmptyActualImageDrawable);

    int numBackgrounds = (builder.getBackgrounds() != null) ? builder.getBackgrounds().size() : 0;
    int numOverlays = (builder.getOverlays() != null) ? builder.getOverlays().size() : 0;
    numOverlays += (builder.getPressedStateOverlay() != null) ? 1 : 0;

    // layer indices and count
    int numLayers = 0;
    int backgroundsIndex = numLayers;
    numLayers += numBackgrounds;
    mPlaceholderImageIndex = numLayers++;
    mActualImageIndex = numLayers++;
    mProgressBarImageIndex = numLayers++;
    mRetryImageIndex = numLayers++;
    mFailureImageIndex = numLayers++;
    int overlaysIndex = numLayers;
    numLayers += numOverlays;

    // array of layers
    Drawable[] layers = new Drawable[numLayers];
    if (numBackgrounds > 0) {
      int index = 0;
      for (Drawable background : builder.getBackgrounds()) {
        layers[backgroundsIndex + index++] = buildBranch(background, null);
      }
    }
    layers[mPlaceholderImageIndex] = buildBranch(
        builder.getPlaceholderImage(),
        builder.getPlaceholderImageScaleType());
    layers[mActualImageIndex] = buildActualImageBranch(
        mActualImageWrapper,
        builder.getActualImageScaleType(),
        builder.getActualImageFocusPoint(),
        builder.getActualImageMatrix(),
        builder.getActualImageColorFilter());
    layers[mProgressBarImageIndex] = buildBranch(
        builder.getProgressBarImage(),
        builder.getProgressBarImageScaleType());
    layers[mRetryImageIndex] = buildBranch(
        builder.getRetryImage(),
        builder.getRetryImageScaleType());
    layers[mFailureImageIndex] = buildBranch(
        builder.getFailureImage(),
        builder.getFailureImageScaleType());
    if (numOverlays > 0) {
      int index = 0;
      if (builder.getOverlays() != null) {
        for (Drawable overlay : builder.getOverlays()) {
          layers[overlaysIndex + index++] = buildBranch(overlay, null);
        }
      }
      if (builder.getPressedStateOverlay() != null) {
        layers[overlaysIndex + index] = buildBranch(builder.getPressedStateOverlay(), null);
      }
    }

    // fade drawable composed of layers
    mFadeDrawable = new FadeDrawable(layers);
    mFadeDrawable.setTransitionDuration(builder.getFadeDuration());

    // rounded corners drawable (optional)
    Drawable maybeRoundedDrawable =
        WrappingUtils.maybeWrapWithRoundedOverlayColor(mFadeDrawable, mRoundingParams);

    // top-level drawable
    mTopLevelDrawable = new RootDrawable(maybeRoundedDrawable);
    mTopLevelDrawable.mutate();

    resetFade();
  }

在获取到图片后,只需调用该类的setImage()函数将图片设置给mActualImageWrapper即可:

  @Override
  public void setImage(Drawable drawable, float progress, boolean immediate) {
    drawable = WrappingUtils.maybeApplyLeafRounding(drawable, mRoundingParams, mResources);
    drawable.mutate();
    mActualImageWrapper.setDrawable(drawable);
    mFadeDrawable.beginBatchMode();
    fadeOutBranches();
    fadeInLayer(mActualImageIndex);
    setProgress(progress);
    if (immediate) {
      mFadeDrawable.finishTransitionImmediately();
    }
    mFadeDrawable.endBatchMode();
  }

上述设置图片的过程是在com/facebook/drawee/controller/AbstractDraweeController.java中定义的:

protected void submitRequest() {
    ...
    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);
            }
          }
         ...
  }

  private void onNewResultInternal(
      String id,
      DataSource<T> dataSource,
      @Nullable T image,
      float progress,
      boolean isFinished,
      boolean wasImmediate) {
   ...
    try {
      drawable = createDrawable(image);
    } catch (Exception exception) {
      logMessageAndImage("drawable_failed @ onNewResult", image);
      releaseImage(image);
      onFailureInternal(id, dataSource, exception, isFinished);
      return;
    }
    T previousImage = mFetchedImage;
    Drawable previousDrawable = mDrawable;
    mFetchedImage = image;
    mDrawable = drawable;
    try {
      // set the new image
      if (isFinished) {
        logMessageAndImage("set_final_result @ onNewResult", image);
        mDataSource = null;
        mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);
        getControllerListener().onFinalImageSet(id, getImageInfo(image), getAnimatable());
        // IMPORTANT: do not execute any instance-specific code after this point
...
  }

其中mSettableDraweeHierarchy.setImage(drawable, 1f, wasImmediate);将图片设置到SimpleDraweeView对应的DraweeHierarchy中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值