使用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中。