Fresco源码分析

Fresco源码分析

Fresco的MVC模型:
M -> DraweeHierarchy 保存和管理图像层次;
V -> DraweeView 显示DraweeHierarchy的顶层图像;
C -> DraweeController 管理其他组件,设置视图层次;
DraweeHolder:controller和hierarchy的持有类,DraweeView 通过此类和controller和hierarchy交互,起到解耦作用。

DraweeHierarchy :Fresco提供了诸如占位图(加载中,加载失败显示的图片),进度条,渐进式JPEG图,多图请求及图片复用等效果,也就是说根据图片加载过程中的不同状态要显示不同效果的图片,所以需要图像的分层管理。

1
2
3
public interface DraweeHierarchy {
Drawable getTopLevelDrawable();
}

DraweeHierarchy代表了一个Drawee的层次,它内部维持了一个树状数据结构,DraweeHierarchy对外屏蔽了一切具体细节,只提供the top level drawable用于DraweeView的动态显示。

SettableDraweeHierarchy:继承自DraweeHierarchy并提供了一些基本的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface SettableDraweeHierarchy extends DraweeHierarchy {
//将图层重置到初始状态
void reset();
//设置目标显示图片
void setImage(Drawable drawable, float progress, boolean immediate);
//设置进度条的进度
void setProgress(float progress, boolean immediate);
//设置加载失败时的图片
void setFailure(Throwable throwable);
//设置加载失败,但用户点击该图片可以重新加载
void setRetry(Throwable throwable);
//设置改图片覆盖已有的图层
void setControllerOverlay(Drawable drawable);
}

以上方法只能由controllers调用!

GenericDraweeHierarchy :实现了SettableDraweeHierarchy的基本方法,是负责装载每个图层信息的载体。

1
public class GenericDraweeHierarchy implements SettableDraweeHierarchy {}


主要有顶级图层,占位符图层,目标显示图层,重新加载图层,显示失败图层, 进度条图层,控制覆盖图层。这些图层的代码位于drawable文件夹。
GenericDraweeHierarchy 的构建是通过GenericDraweeHierarchyBuilder完成的,这是典型的builder设计模式。
在GenericDraweeHierarchyBuilder可以通过一系列set方法设置诸如setPlaceholderImage,setFailureImage,setRetryImage,setProgressBarImage,setBackground(setBackgrounds),setOverlay(setOverlays),setPressedStateOverlay设置以上图层。
GenericDraweeHierarchy在初始化的时候会从GenericDraweeHierarchyBuilder提取出这些设置值。

1
int numLayers = 0;//图层数

经过如下分支:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//backgrounds
int numBackgrounds = (builder.getBackgrounds() != null) ? builder.getBackgrounds().size() : 0;
int backgroundsIndex = numLayers;
numLayers += numBackgrounds;

//placeholder image branch
Drawable placeholderImageBranch = builder.getPlaceholderImage();
if (placeholderImageBranch == null) {
placeholderImageBranch = getEmptyPlaceholderDrawable();
}
placeholderImageBranch = maybeApplyRoundingBitmapOnly(
mRoundingParams,
mResources,
placeholderImageBranch);
placeholderImageBranch = maybeWrapWithScaleType(
placeholderImageBranch,
builder.getPlaceholderImageScaleType());
mPlaceholderImageIndex = numLayers++;
以下分支代码略

将对应的图层依次序存储在数组layers中:

1
2
3
4
5
6
7
8
9
10
11
12
13
Drawable[] layers = new Drawable[numLayers];
if (numBackgrounds > 0) {
int index = 0;
for (Drawable background : builder.getBackgrounds()) {
layers[backgroundsIndex + index++] =
maybeApplyRoundingBitmapOnly(mRoundingParams, mResources, background);
}
}
layers[mPlaceholderImageIndex] = placeholderImageBranch;
layers[mActualImageIndex] = actualImageBranch;
layers[mProgressBarImageIndex] = progressBarImageBranch;
layers[mRetryImageIndex] = retryImageBranch;
layers[mFailureImageIndex] = failureImageBranch;

1
2
3
4
5
6
7
8
9
10
11
//包装成FadeDrawable
mFadeDrawable = new FadeDrawable(layers);
mFadeDrawable.setTransitionDuration(builder.getFadeDuration());

//是否圆角处理
Drawable maybeRoundedDrawable =
maybeWrapWithRoundedOverlayColor(mRoundingParams, mFadeDrawable);

//顶层图片处理
mTopLevelDrawable = new RootDrawable(maybeRoundedDrawable);
mTopLevelDrawable.mutate();

FadeDrawable继承自ArrayDrawable,ArrayDrawable包含了一个Drawables(layers)的数组,会按照数组的顺序进行绘制,角标最大的元素会被绘制在最上层。

1
2
3
4
5
6
7
8
9
@Override
public void draw(Canvas canvas) {
for (int i = 0; i < mLayers.length; i++) {
Drawable drawable = mLayers[i];
if (drawable != null) {
drawable.draw(canvas);
}
}
}

ArrayDrawable还实现了TransformCallback, TransformAwareDrawable 接口,用以获取父图层的变换矩阵。
FadeDrawable还提供了显示/隐藏图层和渐变显示功能:
setTransitionDuration(int durationMs) 设置隐藏/显示图层渐变动画时间。
updateAlphas根据经过和持续时间计算当前的透明度。
fadeInLayer(int index) 显示指定图层。
fadeOutLayer(int index) 隐藏指定图层。
fadeInAllLayers() 显示所有图层。
fadeOutAllLayers() 隐藏所有图层。
fadeToLayer(int index) 显示指定图层同时隐藏其他图层。
fadeUpToLayer(int index) 隐藏数组下标<=index的图层。

RootDrawable继承自ForwardingDrawable,并实现了VisibilityAwareDrawable接口,用以在自身可见度改变的时候的通知函数(onVisibilityChange(boolean visible))和在自身绘制时通知回调(onDraw())。
ForwardingDrawable提供getCurrent()回去当前的容器:

1
2
3
public Drawable getCurrent() {
return mCurrentDelegate;
}

其设计哲学是 ,有助于重用,就像安卓原生的DrawableContainer, LevelListDrawable类那样。

DraweeView用于显示DraweeHierarchy。需要先设置hierarchy和controller。

void init(Context context) 初始化DraweeHolder;
void setHierarchy(DH hierarchy) 设置图层树并显示top level drawable
void setController(@Nullable DraweeController draweeController) 设置DraweeController并显示top level drawable
Drawable getTopLevelDrawable() 获取top level drawable;
View中的onAttachedToWindow()、 onDetachedFromWindow()、 onStartTemporaryDetach()、 onFinishTemporaryDetach() 四个回调函数,提供当视图被绑定/解绑到指定布局上时的回调函数,它们会触发DraweeHolder的onDetach()或onAttach();

设置目标图片是Controller的行为,DraweeView并不支持ImageView的setImageXxx, setScaleType和类似的方法。只有把DraweeView当作是原生的ImageView时才能调用DraweeView的如下方法:
setImageDrawable(Drawable drawable)
setImageBitmap(Bitmap bm)
setImageResource(int resId)
setImageURI(Uri uri)
这是并没有使用到Fresco的缓存和加载机制。

DraweeHolder是包含了controller和hierarchy的持有类。DraweeView的所有方法和操作都是调用了DraweeHolder的相关方法实现的。

void setHierarchy(DH hierarchy)在构造方法和DraweeView.setHierarchy中被调用,将DraweeHierarchy传给持有的DraweeControlle。
setController(@Nullable DraweeController draweeController)无条件解绑旧的Controller,并将旧DraweeController的DraweeHierarchy设置为null,并调用DraweeController.onDetach()将它变为解除绑定状态;将持有的DraweeHierarchy赋给新传入的DraweeController并调用DraweeController.onAttach()让它变为绑定状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);之后发生了什么会在后文中记述。

1
GenericDraweeView extends DraweeView<GenericDraweeHierarchy>

GenericDraweeView就是使用GenericDraweeHierarchy图层树的视图,GenericDraweeHierarchy图层的属性是从XML文件中获取到的。
这些属性如下:

  • Fading animation parameters:
    • @attr ref com.facebook.R.styleable#GenericDraweeView_fadeDuration
    • Images & scale types parameters:
    • @attr ref com.facebook.R.styleable#GenericDraweeView_viewAspectRatio
    • @attr ref com.facebook.R.styleable#GenericDraweeView_placeholderImage
    • @attr ref com.facebook.R.styleable#GenericDraweeView_placeholderImageScaleType
    • @attr ref com.facebook.R.styleable#GenericDraweeView_retryImage
    • @attr ref com.facebook.R.styleable#GenericDraweeView_retryImageScaleType
    • @attr ref com.facebook.R.styleable#GenericDraweeView_failureImage
    • @attr ref com.facebook.R.styleable#GenericDraweeView_failureImageScaleType
    • @attr ref com.facebook.R.styleable#GenericDraweeView_progressBarImage
    • @attr ref com.facebook.R.styleable#GenericDraweeView_progressBarImageScaleType
    • @attr ref com.facebook.R.styleable#GenericDraweeView_progressBarAutoRotateInterval
    • @attr ref com.facebook.R.styleable#GenericDraweeView_actualImageScaleType
    • @attr ref com.facebook.R.styleable#GenericDraweeView_backgroundImage
    • @attr ref com.facebook.R.styleable#GenericDraweeView_overlayImage
    • @attr ref com.facebook.R.styleable#GenericDraweeView_pressedStateOverlayImage
    • Rounding parameters:
    • @attr ref com.facebook.R.styleable#GenericDraweeView_roundAsCircle
    • @attr ref com.facebook.R.styleable#GenericDraweeView_roundedCornerRadius
    • @attr ref com.facebook.R.styleable#GenericDraweeView_roundTopLeft
    • @attr ref com.facebook.R.styleable#GenericDraweeView_roundTopRight
    • @attr ref com.facebook.R.styleable#GenericDraweeView_roundBottomRight
    • @attr ref com.facebook.R.styleable#GenericDraweeView_roundBottomLeft
    • @attr ref com.facebook.R.styleable#GenericDraweeView_roundWithOverlayColor
    • @attr ref com.facebook.R.styleable#GenericDraweeView_roundingBorderWidth
    • @attr ref com.facebook.R.styleable#GenericDraweeView_roundingBorderColor
      aspectRatio 图片的长宽比;

在inflateHierarchy方法中从XML文件中读出相关属性,并将这些属性通过GenericDraweeHierarchyBuilder完成构造,最后通过setHierarchy(builder.build())传递给GenericDraweeView。

而 SimpleDraweeView继承自GenericDraweeView,内部提供了DraweeController的简单实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface Supplier<T> {
T get();
}
private static Supplier<? extends SimpleDraweeControllerBuilder> sDraweeControllerBuilderSupplier;
public static void initialize(
Supplier<? extends SimpleDraweeControllerBuilder> draweeControllerBuilderSupplier) {
sDraweeControllerBuilderSupplier = draweeControllerBuilderSupplier;
}
private void init() {
if (isInEditMode()) {
return;
}
Preconditions.checkNotNull(
sDraweeControllerBuilderSupplier,
"SimpleDraweeView was not initialized!");
mSimpleDraweeControllerBuilder = sDraweeControllerBuilderSupplier.get();
}

setImageURI方法:

1
2
3
4
5
6
7
8
public void setImageURI(Uri uri, @Nullable Object callerContext) {
DraweeController controller = mSimpleDraweeControllerBuilder
.setCallerContext(callerContext)
.setUri(uri)
.setOldController(getController())
.build();
setController(controller);
}

下面到了controller的部分:
The view forwards events to the controller. The controller controls its hierarchy based on those events.

1
2
3
4
5
6
7
8
9
public interface DraweeController {
DraweeHierarchy getHierarchy();
void setHierarchy(@Nullable DraweeHierarchy hierarchy);
void onAttach();
void onDetach();
boolean onTouchEvent(MotionEvent event);
Animatable getAnimatable();

}

1
2
3
4
abstract class AbstractDraweeController<T, INFO> implements
DraweeController,
DeferredReleaser.Releasable,
GestureDetector.ClickListener{}

DraweeView在onAttachedToWindow()方法中调用mDraweeHolder.onAttach();该方法内部调用 mController.onAttach();

1
2
3
4
if (mController != null &&
mController.getHierarchy() != null) {
mController.onAttach();
}

会先判断是否已经给mController设置了hierarchy。
下面我们看AbstractDraweeController的onAttach()方法内部的submitRequest()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
protected void submitRequest() {
getControllerListener().onSubmit(mId, mCallerContext);
mSettableDraweeHierarchy.setProgress(0, true);
mIsRequestSubmitted = true;
mHasFetchFailed = false;
mDataSource = getDataSource();
final String id = mId;
final boolean wasImmediate = mDataSource.hasResult();
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);
}
};
mDataSource.subscribe(dataSubscriber, mUiThreadImmediateExecutor);
}

发现这是观察者设计模式,DataScriber订阅了DataSource的信息,在不同的回调函数中onNewResultImpl,onFailureImpl,onProgressUpdate对DraweeHierarchy做出相应的处理。
下面是图片加载失败情况下的主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (isFinished) {
mDataSource = null;
mHasFetchFailed = true;
if (mRetainImageOnFailure && mDrawable != null) {
mSettableDraweeHierarchy.setImage(mDrawable, 1f, true);
} else if (shouldRetryOnTap()) {
mSettableDraweeHierarchy.setRetry(throwable);
} else {
mSettableDraweeHierarchy.setFailure(throwable);
}
getControllerListener().onFailure(mId, throwable);
} else {
logMessageAndFailure("intermediate_failed @ onFailure", throwable);
getControllerListener().onIntermediateImageFailed(mId, throwable);
}

AbstractDraweeController是通过AbstractDraweeControllerBuilder的build()方法构建的。
其重要方法还有Supplier> obtainDataSourceSupplier(),
获取被controller使用的the top-level数据源的supplier 。

参考文章:https://github.com/desmond1121/Fresco-Source-Analysis

文章目录
  1. 1. Fresco源码分析