Fresco源码分析之Hierarchy

上篇文章咱们分析了Fresco中的DraweeView,对其中的一些原理以及方法进行了解析。在这过程当中咱们了解到,DraweeView中是经过DraweeHolder来统一管理的。而DraweeHolder又是用来统一管理相关的HierarchyController,若是想了解DraweeView相关的知识,能够先看下个人前一篇文章Fresco源码分析之DraweeView。今天这里进一步来分析Fresco中的Hierarchyphp

GenericDraweeHierarchyBuilder

GenericDraweeView的构造方法中会调用*inflateHierarchy(context, atts)*方法来建立一个GenericDraweeHierarchyBuilder对象,经过调用该对象的build方法来生成一个Hierarchyandroid

若是你看了我上一篇文章,相信对这个方法你会感到很亲切。git

因此这个类的主要做用是用来建立一个Hierarchy,经过builder模式来实例化包含相关信息的Hierarchy。若是你一步步深刻了解Fresco的话,相信你对builder模式也将习觉得常,由于后面你将常常与它碰面。这也是Fresco开源项目的主要设计模式。在该builder类中主要构建的信息有:github

  • Drawable相关:placeholderImageretryImagefailureImageprogressBarImagebackgroundoverlayspressedStateOverlay
  • ScaleType相关:与Drawable相对应的placeholderImageScaleTyperetryImageScaleType等等。
  • 其它:fadeDuration渐变过渡时间与RoundingParams圆角相关信息等等

GenericDraweeHierarchy

经过上面的GenericDraweeHierarchyBuilderbuild方法会建立一个GenericDraweeHierarchy对象。这就是咱们今天须要主要分析的类,也是Fresco的一个核心类。canvas

SettableDraweeHierarchy

首先咱们来分析一下它的类结构,它会实现SettableDraweeHierarchy接口,咱们进入该接口发现一共有6个接口方法,它们分别为:设计模式

  1. void reset(); 从新初始化Hierarchy
  2. void setImage(Drawable drawable, float progress, boolean immediate); 设置实际须要展现的图片,其中progress表示图片的加载质量进度(在渐进式中会使用到)
  3. void setProgress(float progress, boolean immediate); 更新图片加载进度
  4. void setFailure(Throwable throwable); 图片加载失败时调用,能够设置failureImage
  5. void setRetry(Throwable throwable); 当图片加载失败时从新进行加载,能够设置retryImage
  6. void setControllerOverlay(Drawable drawable); 用来设置图层覆盖

这些方法在GenericDraweeHierarchy中都会作出相应的实现,同时最终都会在对应的DraweeView中的Controller来调用。数组

至于这些方法中的实现细节,因为代码比较多,这里就不一一列举出来,你们能够自行查看GenericDraweeHierarchy的源码。缓存

DraweeHierarchy

上面的SettableDraweeHierarchy还有一个父类接口为DraweeHierarchy,在这个接口中只有一个接口方法为Drawable getTopLevelDrawable();。是否是对这个方法也有点熟悉呢?(路人甲:嗯,好像上篇文章说起过!)它是用来获取视图树的最顶层视图,其实说白了就是显示出来的Drawable。它主要在DraweeHolder中调用,最终也会由void setHierarchy(DH hierarchy)void setController(@Nullable DraweeController draweeController) 来调用bash

super.setImageDrawable(mDraweeHolder.getTopLevelDrawable());
复制代码

从而显示须要展现的图片。微信

下面咱们回到以前的GenericDraweeHierarchy类。在开始分析它以前,咱们先来了解一下它的另外一个感念-层级树或者说图层树,我这里就把它说成图层树吧。那么咱们来看下Fresco的图层树是什么样的:

*  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
复制代码

根据上面所展现的层次结构,咱们能够发现最底层是由RootDrawable来构成,它有一个直接子分支为FadeDrawable。而在FadeDrawable中又有5个直接子分支,分别为placeholder branchactual image branchprogressBar image branchretry image branchfailure image branch。至于这些image的做用相信不用我再多作说明了,这些image除了actual image是必需要明确指定的,其它的都是可选择的配置。所以RootDrawableFadeDrawable是必定存在的。虽然其它的都是可选的配置,但不管你是否选择了,它们的层级结构都会保留在图层树中。还有每个层级都有本身独立的scale type,固然rounding(圆角)也是支持的。

其实除了这5个分支,与它们同一层次的还有background imageoverlay image,它们分别位于placeholder image以前与failure image以后。background相信都知道,由于图片都支持backgroundsrcoverlay为图层覆盖。至于为何没有在上面的结构中显示,我这里也不得而知,猜想多是这两个并非Fresco主要经常使用的特性。

那么这些图层结构是经过layers数组来体现的,能够来看下GenericDraweeHierarchy的源码

GenericDraweeHierarchy(GenericDraweeHierarchyBuilder builder) {
    mResources = builder.getResources();
    mRoundingParams = builder.getRoundingParams();
 
    mActualImageWrapper = new ForwardingDrawable(mEmptyActualImageDrawable);
 
    int numOverlays = (builder.getOverlays() != null) ? builder.getOverlays().size() : 1;
    numOverlays += (builder.getPressedStateOverlay() != null) ? 1 : 0;
 
    // layer indices and count
    int numLayers = OVERLAY_IMAGES_INDEX + numOverlays;
 
    // array of layers
    Drawable[] layers = new Drawable[numLayers];
    layers[BACKGROUND_IMAGE_INDEX] = buildBranch(builder.getBackground(), null);
    layers[PLACEHOLDER_IMAGE_INDEX] = buildBranch(
        builder.getPlaceholderImage(),
        builder.getPlaceholderImageScaleType());
    layers[ACTUAL_IMAGE_INDEX] = buildActualImageBranch(
        mActualImageWrapper,
        builder.getActualImageScaleType(),
        builder.getActualImageFocusPoint(),
        builder.getActualImageColorFilter());
    layers[PROGRESS_BAR_IMAGE_INDEX] = buildBranch(
        builder.getProgressBarImage(),
        builder.getProgressBarImageScaleType());
    layers[RETRY_IMAGE_INDEX] = buildBranch(
        builder.getRetryImage(),
        builder.getRetryImageScaleType());
    layers[FAILURE_IMAGE_INDEX] = buildBranch(
        builder.getFailureImage(),
        builder.getFailureImageScaleType());
    if (numOverlays > 0) {
      int index = 0;
      if (builder.getOverlays() != null) {
        for (Drawable overlay : builder.getOverlays()) {
          layers[OVERLAY_IMAGES_INDEX + index++] = buildBranch(overlay, null);
        }
      } else {
        index = 1; // reserve space for one overlay
      }
      if (builder.getPressedStateOverlay() != null) {
        layers[OVERLAY_IMAGES_INDEX + 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();
  }
复制代码

根据源码能够明显的看出无论overlay是否存在,它都会保留图层层次,固然若是存在的话,就根据实际状况在OVERLAY_IMAGES_INDEX以后增长图层层次。layers是一个Drawable数组,这里经过Drawable buildBranch(@Nullable Drawable drawable, @Nullable ScaleType scaleType) 方法来建立对应的Drawable。在最后,建立了FadeDrawable并将layers传递给它,在这里FadeDrawable的特性应该有点眉目了吧,其实它内部作的就是对Drawable数组进行操做。以后RootDrawable也出现了(关于Drawable文章后面会统一分析),这样以前所提到的层级结构就造成了。

既然actual image是必定存在的,那么在它真正展现以前image中的显示用什么来控制的呢?其实就是咱们以前所提到的SettableDraweeHierarchy来控制。在真实的图片展现出来以前,它能够用来展现placeholder image等相关图层。具体的咱们能够来看一下它里面实现的一些方法。

setImage

这里就拿void setImage(Drawable drawable, float progress, boolean immediate) 来进行深刻分析,那么下面来看下它的源码:

@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(ACTUAL_IMAGE_INDEX);
    setProgress(progress);
    if (immediate) {
      mFadeDrawable.finishTransitionImmediately();
    }
    mFadeDrawable.endBatchMode();
  }
复制代码

首先该方法会使用WrappingUtils工具类而且结合RoundingParams参数来从新生成一个能够附带圆角的Drawable。而后将新的Drawable交由mActualImageWrapper,结合上面的层次结果分析,会很容易知道这就是须要真实显示的图层。它是一个ForwardingDrawable类型,该类主要是对传入的目标Drawable进行相应的原生方法操做。下一步调用FadeDrawable的*beginBatchMode()方法,该方法的做用主要为了图层批处理作标记,防止在批处理时进行invalidate操做。直到最后的endBatchMode()方法调用以后才标识着图层批处理操做结束。该批处理操做的目的是将actual image图层显示出来,因此首先调用fadeOutBranches()*方法:

private void fadeOutBranches() {
    fadeOutLayer(PLACEHOLDER_IMAGE_INDEX);
    fadeOutLayer(ACTUAL_IMAGE_INDEX);
    fadeOutLayer(PROGRESS_BAR_IMAGE_INDEX);
    fadeOutLayer(RETRY_IMAGE_INDEX);
    fadeOutLayer(FAILURE_IMAGE_INDEX);
  }
复制代码

这里对上述提到的全部图层进行*fadeOutLayer()操做,继续进入fadeOutLayer()*方法

private void fadeOutLayer(int index) {
    if (index >= 0) {
      mFadeDrawable.fadeOutLayer(index);
    }
  }
复制代码

发现也很简单,无非就是调用了FadeDrawable的*fadeOutLayer()*方法。

public void fadeOutLayer(int index) {
    mTransitionState = TRANSITION_STARTING;
    mIsLayerOn[index] = false;
    invalidateSelf();
  }
复制代码

在以前已经提到过FadeDrawable本质上能够理解为时一个Drawable数组,内部都是围绕着数组总体进行操做。对应的还有fadeInLayer()

private void fadeInLayer(int index) {
    if (index >= 0) {
      mFadeDrawable.fadeInLayer(index);
    }
  }
复制代码

FadeDrawable中除了用来保存相应的Drawable数组mLayers,还有与其相对应的mIsLayerOn布尔数组,该数组用来标识各个Hierarchy中的图层是否须要展现。因此*fadeOutLayer()是对index处的图层进行隐藏标识。最终的显隐操做都会转化为在void draw(Canvas canvas)*方法中进行alpha操做。

那么再回到以前的*setImage()*方法中,*fadeOutBranches()是对相关的图层进行隐藏标识,而后再经过fadeInLayer(ACTUAL_IMAGE_INDEX)方法改变actual image图层的标识,将它改变成显示状态。最后若是有progressBar image图层的话,也将会由setProgress(progress)*方法来体现。immediate是用来判断是否以后的显隐操做立马实现或者渐变过渡实现(内部就是对alpha进行百分比操做,内部有个mDurationMs,该时间值也是文章开头提到的builder中的fadeDuration)。这样整个的实际图片展现流程咱们已经分析完毕,因此Hierarchy中最重要的仍是对图层概念的理解。下面再对GenericDraweeHierarchy中的一些其它方法进行简要的说明:

  • Drawable buildActualImageBranch(Drawable drawable, @Nullable ScaleType scaleType, @Nullable PointF focusPoint, @Nullable ColorFilter colorFilter) 构建实际展现图片的图层分支,内部对于Drawable的建立仍是借助WrappingUtils工具类
  • Drawable buildBranch(@Nullable Drawable drawable, @Nullable ScaleType scaleType) 构建除实际展现图片以外的其它图层分支,Drawable的建立也是借助WrappingUtils工具类
  • void resetFade() 初始化图层结构

主要的就这几个吧,其它的都已经在上面分析流程中详细说明了。

Drawable

最后再整理一下Fresco中的一些相关的自定义的Drawable子类

  1. ArrayDrawableDrawable数组的集合体,经过layers数组来管理Drawable,内部的都是对数组集合中的每个Drawable进行操做,相似与Android原生的LayerDrawable,只是它并不支持addremove操做
  2. ForwardingDrawable:对传入的目标Drawable即操做对象进行封装处理,该新类的方法能够调用目标对象对应的方法,同时保留目标Drawable的各个状态,不依赖与目标类的细节实现,提升新类的稳定性,该方式能够称之为复合。Fresco中绝大多数自定义Drawable都是它的子类。
  3. AutoRotateDrawable:它继承于ForwardingDrawable,实现的是对Drawable的旋转操做
  4. FadeDrawable:它继承于ArrayDrawable,以前也详细提到过,Hierarchy中的主要图层集合体。主要是经过mLayersmIsLayerOn数组来控制数组中各个Drawablealpha值,即显隐
  5. MatrixDrawable:它继承于ForwardingDrawable,顾名思义经过矩阵来改变Drawable状态。
  6. OrientedDrawable:它也继承于ForwardingDrawable,它不一样于AutoRotateDrawable的是,它只支持90度的倍数角度旋转。
  7. ProgressBarDrawable:进度条Drawable,支持横竖方向。
  8. RoundedBitmapDrawable:继承于BitmapDrawable,根据Bitmap来建立有关圆角的Drawable,主要在WrappingUtils类中使用,用例构建全新的圆角Drawable
  9. RoundedColorDrawable:继承于Drawable,根据Color来建立圆角Drawable,主要在WrappingUtils类中使用,用例构建全新的圆角Drawable
  10. RoundedCornersDrawable:继承于ForwardingDrawable,用于设计overLay覆盖图片圆角,主要在WrappingUtils类中使用
  11. ScaleTypeDrawable:继承于ForwardingDrawable,用于缩放类型的Drawable

End

本篇文章主要是分析Fresco中有关Hierarchy相关的实现与原理,经过分析发现Hierarchy中都是对Drawable图层进行处理,并无其它的缓存、请求之类的逻辑。因此若是你使用Fresco的时候只使用Hierarchy的话,就与别的ImageView没有多大的区别,真正的图层操做与缓存控制都在Controller中,因此下篇文章将进入Controller解析,来详细了解它的实现细节。

Fresco源码分析系列Github地址

Recommend

Android共享动画兼容实现

Kotlin最佳实践

RecyclerView下拉刷新与上拉更多

Android高仿微信之mvp实现(四)

php与android的简单交互

tensorflow-梯度降低,有这一篇就足够了

博客

相关文章
相关标签/搜索