简述
接上部分《Glide v4 源码浅析(3)-into(上)》,继续分析获取到源数据后,对数据进行解码转换处理,并最终设置到ImageView上。
源码分析
decode和transcode
进入decodeFromData方法:
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
// 又调用decodeFromFetcher方法解码数据。
Resource<R> result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
// 释放fetcher中占用的资源。
fetcher.cleanup();
}
}
接着看decodeFromFetcher方法:
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
// getLoadPath方法在前面执行过,已经存有dataClass对应的LoadPath,这里可以快速获取。
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
继续看runLoadPath方法:
private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
LoadPath<Data, ResourceType, R> path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
// 根据Data类型从DataRewinderRegistry检索对应的DataRewinder。这里的Data类型为MappedByteBuffer,
// 因此rewinder实例为ByteBufferRewinder。
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
LoadPath.load中又调用了loadWithExceptionList方法:
private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
@NonNull Options options,
int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions) throws GlideException {
Resource<Transcode> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
// 依次取出DecodePath进行解码尝试。
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
// 若成功解码数据,就结束循环,返回结果。
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
进入DecodePath的decode方法:
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
// 通过之前收集好的ResourceDecoder尝试解码,获得解码后资源。
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
// 回调给DecodeJob的onResourceDecoded方法,会根据宽高、缩放、裁剪类型返回转换后的资源。
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
// 返回使用ResourceTranscoder转码后的资源。
return transcoder.transcode(transformed, options);
}
该方法中分三步对资源进行处理:
- 使用ResourceDecoder进行解码;
- 使用Transformation进行转换;
- 使用ResourceTranscoder进行转码。
一. decodeResource
先看第一步,decodeResource中又调用了decodeResourceWithList方法:
@NonNull
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
Resource<ResourceType> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decoders.size(); i < size; i++) {
// 依次尝试ResourceDecoder。
ResourceDecoder<DataType, ResourceType> decoder = decoders.get(i);
try {
// 本例中rewinder为ByteBufferRewinder,返回position置0后的MappedByteBuffer。
DataType data = rewinder.rewindAndGet();
// 初步匹配类型和选项。
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
// 本例中可用的decoder为ByteBufferBitmapDecoder,直接看它的decode方法。
result = decoder.decode(data, width, height, options);
}
// Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but
// instead log and continue. See #2406 for an example.
} catch (IOException | RuntimeException | OutOfMemoryError e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to decode data for " + decoder, e);
}
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
// 返回解码成功的资源。
return result;
}
进入ByteBufferBitmapDecoder的decode方法:
@Override
public Resource<Bitmap> decode(@NonNull ByteBuffer source, int width, int height,
@NonNull Options options)
throws IOException {
// toStream方法返回ByteBufferStream(继承自InputStream),包装了ByteBuffer。
InputStream is = ByteBufferUtil.toStream(source);
// 从传入的is中解码出Bitmap。
return downsampler.decode(is, width, height, options);
}
Downsampler的decode方法最终调用了:
public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
Options options, DecodeCallbacks callbacks) throws IOException {
// ByteBufferStream重写了markSupported方法返回true。
Preconditions.checkArgument(is.markSupported(), "You must provide an InputStream that supports"
+ " mark()");
// 分配解码时的临时空间。
byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
bitmapFactoryOptions.inTempStorage = bytesForOptions;
// 获取解码格式。
DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
// 获取采样策略。
DownsampleStrategy downsampleStrategy = options.get(DownsampleStrategy.OPTION);
// bitmap尺寸是否适配目标尺寸。
boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
// 是否启用硬件位图。
boolean isHardwareConfigAllowed =
options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG);
try {
// 解码得到Bitmap。
Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
downsampleStrategy, decodeFormat, isHardwareConfigAllowed, requestedWidth,
requestedHeight, fixBitmapToRequestedDimensions, callbacks);
// 对Bitmap包装并返回。
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions);
}
}
接着看decodeFromWrappedStreams方法:
private Bitmap decodeFromWrappedStreams(InputStream is,
BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
int requestedHeight, boolean fixBitmapToRequestedDimensions,
DecodeCallbacks callbacks) throws IOException {
long startTime = LogTime.getLogTime();
// 通过设置options.inJustDecodeBounds为true,再调用BitmapFactory.decodeStream,获得原始数据的尺寸。
int[] sourceDimensions = getDimensions(is, options, callbacks, bitmapPool);
int sourceWidth = sourceDimensions[0];
int sourceHeight = sourceDimensions[1];
String sourceMimeType = options.outMimeType;
// If we failed to obtain the image dimensions, we may end up with an incorrectly sized Bitmap,
// so we want to use a mutable Bitmap type. One way this can happen is if the image header is so
// large (10mb+) that our attempt to use inJustDecodeBounds fails and we're forced to decode the
// full size image.
if (sourceWidth == -1 || sourceHeight == -1) {
isHardwareConfigAllowed = false;
}
// 从图片文件header中获取exif信息,通过exif读取方向信息。
int orientation = ImageHeaderParserUtils.getOrientation(parsers, is, byteArrayPool);
// 获取图像旋转角度。
int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
// 若exif方法信息指明有旋转/翻转,则为true。
boolean isExifOrientationRequired = TransformationUtils.isExifOrientationRequired(orientation);
// 确定目标尺寸。
int targetWidth = requestedWidth == Target.SIZE_ORIGINAL ? sourceWidth : requestedWidth;
int targetHeight = requestedHeight == Target.SIZE_ORIGINAL ? sourceHeight : requestedHeight;
// 获取图片格式。
ImageType imageType = ImageHeaderParserUtils.getType(parsers, is, byteArrayPool);
// 根据图片旋转角度和ImageView的mScaleType缩放策略(outside、inside、fit等)和原始尺寸与目标尺寸比计算inSampleSize。
// 再根据图片格式计算inTargetDensity和inDensity,并根据inTargetDensity和inDensity的值设置inScaled。
calculateScaling(
imageType,
is,
callbacks,
bitmapPool,
downsampleStrategy,
degreesToRotate,
sourceWidth,
sourceHeight,
targetWidth,
targetHeight,
options);
// 判断是否支持硬件位图,设置inPreferredConfig和inMutable。
calculateConfig(
is,
decodeFormat,
isHardwareConfigAllowed,
isExifOrientationRequired,
options,
targetWidth,
targetHeight);
boolean isKitKatOrGreater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
// 判断是否能够使用inBitmap(内存块复用)。低于4.4版本需要找到相同大小的bitmap才能复用,4.4+的允许大于尺寸的bitmap。
if ((options.inSampleSize == 1 || isKitKatOrGreater) && shouldUsePool(imageType)) {
int expectedWidth;
int expectedHeight;
if (sourceWidth >= 0 && sourceHeight >= 0
&& fixBitmapToRequestedDimensions && isKitKatOrGreater) {
expectedWidth = targetWidth;
expectedHeight = targetHeight;
} else {
float densityMultiplier = isScaling(options)
? (float) options.inTargetDensity / options.inDensity : 1f;
int sampleSize = options.inSampleSize;
int downsampledWidth = (int) Math.ceil(sourceWidth / (float) sampleSize);
int downsampledHeight = (int) Math.ceil(sourceHeight / (float) sampleSize);
expectedWidth = Math.round(downsampledWidth * densityMultiplier);
expectedHeight = Math.round(downsampledHeight * densityMultiplier);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Calculated target [" + expectedWidth + "x" + expectedHeight + "] for source"
+ " [" + sourceWidth + "x" + sourceHeight + "]"
+ ", sampleSize: " + sampleSize
+ ", targetDensity: " + options.inTargetDensity
+ ", density: " + options.inDensity
+ ", density multiplier: " + densityMultiplier);
}
}
// If this isn't an image, or BitmapFactory was unable to parse the size, width and height
// will be -1 here.
if (expectedWidth > 0 && expectedHeight > 0) {
// 设置inBitmap,其中还会判断inPreferredConfig,不能为HARDWARE。
setInBitmap(options, bitmapPool, expectedWidth, expectedHeight);
}
}
// 设置完Options后,通过BitmapFactory.decodeStream获得Bitmap。
Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
// 该回调中默认什么都不执行。
callbacks.onDecodeComplete(bitmapPool, downsampled);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logDecode(sourceWidth, sourceHeight, sourceMimeType, options, downsampled,
requestedWidth, requestedHeight, startTime);
}
Bitmap rotated = null;
if (downsampled != null) {
// If we scaled, the Bitmap density will be our inTargetDensity. Here we correct it back to
// the expected density dpi.
downsampled.setDensity(displayMetrics.densityDpi);
// 设置Matrix,通过Canvas.drawBitmap对Bitmap进行旋转/翻转。
rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
if (!downsampled.equals(rotated)) {
bitmapPool.put(downsampled);
}
}
return rotated;
}
在Downsampler中经过缩放、设置采样率、旋转/翻转等操作,最终将InputStream转换成Bitmap返回。
二. transform
回到DecodePath的decode方法中,接下来将通过回调,将Bitmap传到DecodeJob的onResourceDecoded中,进行transform处理:
@Synthetic
@NonNull
<Z> Resource<Z> onResourceDecoded(DataSource dataSource,
@NonNull Resource<Z> decoded) {
@SuppressWarnings("unchecked")
// 此时decoded为包装了Bitmap的BitmapResource对象,返回的是Bitmap类型。
Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
// 此时dataSource为REMOTE。
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
// 获取Bitmap.class对应的Transformation。
// ImageView的缩放类型默认为FIT_CENTER,此时获取相对应的FitCenter实例。
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
// 这里根据FIT_CENTER缩放策略计算宽高,然后从bitmapPool中取一块复用的Bitmap内存块,接着设置Matrix,最后通过canvas.drawBitmap绘制。
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
// TODO: Make this the responsibility of the Transformation.
// 判断是否有生成新的Bitmap,如果根据缩放策略计算的宽高是一致的,则不会生成新的。
if (!decoded.equals(transformed)) {
// 将变换前的Bitmap存入bitmapPool中,留待下次复用内存块。
decoded.recycle();
}
final EncodeStrategy encodeStrategy;
final ResourceEncoder<Z> encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
// 获取在ResourceEncoderRegistry在注册的Bitmap.class对应的BitmapEncoder。
encoder = decodeHelper.getResultEncoder(transformed);
// 获取到对应的EncodeStrategy.TRANSFORMED。
encodeStrategy = encoder.getEncodeStrategy(options);
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
Resource<Z> result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
// 这里判断资源是否可缓存,此时判断为false,跳过。
if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
encodeStrategy)) {
if (encoder == null) {
throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
}
// 生成新key。
final Key key;
switch (encodeStrategy) {
case SOURCE:
key = new DataCacheKey(currentSourceKey, signature);
break;
case TRANSFORMED:
key =
new ResourceCacheKey(
decodeHelper.getArrayPool(),
currentSourceKey,
signature,
width,
height,
appliedTransformation,
resourceSubClass,
options);
break;
default:
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
// 用于在将转码后的资源返回给外部调用者后,再对资源进行编码保存。
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
// 这里不执行上面if方法体,返回转换后的资源。
return result;
}
该方法中,再通过Transformation对资源进行了依次转换。
三. transcode
回到DecodePath的decode方法中,资源经过transform后,传给ResourceTranscoder进行transcode,进入BitmapDrawableTranscoder:
@Nullable
@Override
public Resource<BitmapDrawable> transcode(@NonNull Resource<Bitmap> toTranscode,
@NonNull Options options) {
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
使用LazyBitmapDrawableResource对象再进行一层包装。
通知资源就绪
decode方法中经过解码转换,最终创建包装了对Bitmap层层包装后的LazyBitmapDrawableResource实例,一直返回到DecodeJob的decodeFromRetrievedData方法中。
private void decodeFromRetrievedData() {
···
// 此时resource为LazyBitmapDrawableResource实例。
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
看notifyEncodeAndRelease方法:
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
// 这里为false。
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
// 通知资源就绪。
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
// 这里为false,不执行编码保存磁盘操作。
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
// 最后释放此DecodeJob的资源,解除成员变量的引用。
onEncodeComplete();
}
notifyComplete方法中回调了EngineJob的onResourceReady方法:
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
// 此时resource为LazyBitmapDrawableResource。
this.resource = resource;
// 此时dataSource为REMOTE。
this.dataSource = dataSource;
// 通过主线程handler发送到主线程中处理。
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
看MAIN_THREAD_HANDLER注册的Handler.Callback中的handleMessage方法:
@Override
publicboolean handleMessage(Message message) {
EngineJob<?> job = (EngineJob<?>) message.obj;
// 此时的what为MSG_COMPLETE。
switch (message.what) {
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
case MSG_EXCEPTION:
job.handleExceptionOnMainThread();
break;
case MSG_CANCELLED:
job.handleCancelledOnMainThread();
break;
default:
throw new IllegalStateException("Unrecognized message: " + message.what);
}
return true;
}
看handleResultOnMainThread方法:
@Synthetic
void handleResultOnMainThread() {
stateVerifier.throwIfRecycled();
if (isCancelled) {
// 判断若取消了本次加载,将Bitmap放入bitmapPool中。
resource.recycle();
// 释放此EngineJob的资源。
release(false /*isRemovedFromQueue*/);
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
// 重新EngineResource,包装加载到的资源。
engineResource = engineResourceFactory.build(resource, isCacheable);
// hasResource标识置为true。
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of
// notifying if it synchronously released by one of the callbacks.
// 增加资源引用计数。
engineResource.acquire();
// 回调到Engine中:
// 1.给engineResource添加资源释放监听器;
// 2.若可缓存,加入活动资源缓存集合。
// 3.从当前请求任务集合移出。
listener.onEngineJobComplete(this, key, engineResource);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = cbs.size(); i < size; i++) {
// 遍历保存的回调,即依次通知重复请求的外部调用者。
ResourceCallback cb = cbs.get(i);
// 排除已取消的调用者回调。
if (!isInIgnoredCallbacks(cb)) {
// 增加活动资源引用计数。
engineResource.acquire();
// 回调通知资源就绪。
cb.onResourceReady(engineResource, dataSource);
}
}
// Our request is complete, so we can release the resource.
// 引用计数剪一。
// 若上面仍在监听的回调为空,则表明没有在活动的调用者引用此资源。
// 将通知Engine将该资源从活动资源集合中移除,然后将该资源加入内存缓存集合或者释放该资源。
engineResource.release();
// 释放EngineJob的资源。
release(false /*isRemovedFromQueue*/);
}
这里的ResourceCallback为SingleRequest实例,看它的onResourceReady方法:
@Override
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
loadStatus = null;
if (resource == null) {
GlideException exception = new GlideException("Expected to receive a Resource<R> with an "
+ "object of " + transcodeClass + " inside, but instead got null.");
onLoadFailed(exception);
return;
}
// 取出包装的BitmapDrawable。
Object received = resource.get();
// 检查返回的资源对象类型是否匹配。
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
GlideException exception = new GlideException("Expected to receive an object of "
+ transcodeClass + " but instead" + " got "
+ (received != null ? received.getClass() : "") + "{" + received + "} inside" + " "
+ "Resource{" + resource + "}."
+ (received != null ? "" : " " + "To indicate failure return a null Resource "
+ "object, rather than a Resource object containing null data."));
onLoadFailed(exception);
return;
}
// requestCoordinator默认为null,canSetResource返回true。
if (!canSetResource()) {
releaseResource(resource);
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady((Resource<R>) resource, (R) received, dataSource);
}
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
// requestCoordinator默认为null,因此isFirstReadyResource返回true。
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (glideContext.getLogLevel() <= Log.DEBUG) {
Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "
+ dataSource + " for " + model + " with size [" + width + "x" + height + "] in "
+ LogTime.getElapsedMillis(startTime) + " ms");
}
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
// requestListeners默认为空。
if (requestListeners != null) {
for (RequestListener<R> listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
// targetListener默认为空,因此anyListenerHandledUpdatingTarget为false。
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
// 若上面listener有一个返回true,就不再执行target的回调。
if (!anyListenerHandledUpdatingTarget) {
// 创建Transition,用于显示动画效果,默认为NoTransition,不做任何动效。
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
// 回调target资源就绪。
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
// 通知加载请求成功。若有requestCoordinator,将递归调用下一个请求。
notifyLoadSuccess();
}
这里的target为在调用RequestBuilder的into方法时创建的DrawableImageViewTarget,这里回调它父类ImageViewTarget的onResourceReady:
@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
// transition默认为空,不做动效,直接展示。
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
接着看setResourceInternal方法:
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
// 设置资源。
setResource(resource);
// 判断资源对象是否实现了Animatable,若支持动画播放,则会播放。
maybeUpdateAnimatable(resource);
}
setResource为抽象方法,在DrawableImageViewTarget中实现:
@Override
protected void setResource(@Nullable Drawable resource) {
// 调用了ImageView的setImageDrawable方法,将BitmapDrawable设置上去展示。
view.setImageDrawable(resource);
}
可以看到最终将BitmapDrawable传给了ImageView展示。
总结
以上就是Glide的最简单的使用例子的执行流程。