加载完图片后,需要一层层向上返回 返回路径 由于隔得太远,我重新把(分析23) 继续返回到 所以,分析15 继续向上返回,最终返回到 重新贴出这部分代码 终于,静图 / Gif图 成功显示出来 至此, 一图总结Glide
,该功能非常强大 Android
图片加载开源框架 相信大家并不陌生 // 步骤3:若是普通静图,就调用decodeBitmapWrapper()解码
if (result == null) {
ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());
result = decodeBitmapWrapper(forBitmapDecoder, width, height);
// ->>分析23
}
return result;
}
<-- 分析23:decodeBitmapWrapper() -->
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource
// bitmapDecoder是一个ImageVideoBitmapDecoder对象
// 即调用ImageVideoBitmapDecoder对象的decode()->>分析24
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
}
return result;
}
...
}
<-- 分析24:ImageVideoBitmapDecoder.decode() -->
public class ImageVideoBitmapDecoder implements ResourceDecoder<ImageVideoWrapper, Bitmap> {
...
@Override
public Resource
Resource
InputStream is = source.getStream();
// 步骤1:获取到服务器返回的InputStream
if (is != null) {
try {
result = streamDecoder.decode(is, width, height);
// 步骤2:调用streamDecoder.decode()进行解码
// streamDecode是一个StreamBitmapDecoder对象 ->>分析25
} catch (IOException e) {
...
}
<-- 分析25:StreamBitmapDecoder.decode() -->
public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {
...
@Override
public Resource
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
// Downsampler的decode() ->>分析26
// 从分析26回来看这里:
return BitmapResource.obtain(bitmap, bitmapPool);
// 作用:将分析26中返回的Bitmap对象包装成Resource
// 因为decode()返回的是一个Resource
// 经过这样一层包装后,如果还需要获取Bitmap,只需要调用Resource
// 接下来,我们需要一层层地向上返回(请向下看直到跳出该代码块)
}
...
}
<-- 分析26:downsampler.decode() -->
// 主要作用:读取服务器返回的InputStream & 加载图片
// 其他作用:对图片的压缩、旋转、圆角等逻辑处理
public abstract class Downsampler implements BitmapDecoder<InputStream> {
...
@Override
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
final ByteArrayPool byteArrayPool = ByteArrayPool.get();
final byte[] bytesForOptiOns= byteArrayPool.getBytes();
final byte[] bytesForStream = byteArrayPool.getBytes();
final BitmapFactory.Options optiOns= getDefaultOptions();
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(
is, bytesForStream);
// Use to retrieve exceptions thrown while reading.
// TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a way to determine
// if a Bitmap is partially decoded, consider removing.
ExceptionCatchingInputStream exceptiOnStream=
ExceptionCatchingInputStream.obtain(bufferedStream);
// Use to read data.
// Ensures that we can always reset after reading an image header so that we can still attempt to decode the
// full image even when the header decode fails and/or overflows our read buffer. See #283.
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
try {
exceptionStream.mark(MARK_POSITION);
int orientation = 0;
try {
orientation = new ImageHeaderParser(exceptionStream).getOrientation();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot determine the image orientation from header", e);
}
} finally {
try {
exceptionStream.reset();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot reset the input stream", e);
}
}
}
options.inTempStorage = bytesForOptions;
final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
final int inWidth = inDimens[0];
final int inHeight = inDimens[1];
final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
final Bitmap downsampled =
downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize,
decodeFormat);
// BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
// and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
// we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
final Exception streamException = exceptionStream.getException();
if (streamException != null) {
throw new RuntimeException(streamException);
}
Bitmap rotated = null;
if (downsampled != null) {
rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
downsampled.recycle();
}
}
return rotated;
} finally {
byteArrayPool.releaseBytes(bytesForOptions);
byteArrayPool.releaseBytes(bytesForStream);
exceptionStream.release();
releaseOptions(options);
}
}
private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
DecodeFormat decodeFormat) {
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
Bitmap.Config cOnfig= getConfig(is, decodeFormat);
options.inSampleSize = sampleSize;
options.inPreferredCOnfig= config;
if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
// BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
}
return decodeStream(is, bufferedStream, options);
}
/**
* A method for getting the dimensions of an image from the given InputStream.
*
* @param is The InputStream representing the image.
* @param options The options to pass to
* {@link BitmapFactory#decodeStream(InputStream, android.graphics.Rect,
* BitmapFactory.Options)}.
* @return an array containing the dimensions of the image in the form {width, height}.
*/
public int[] getDimensions(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
options.inJustDecodeBounds = true;
decodeStream(is, bufferedStream, options);
options.inJustDecodeBounds = false;
return new int[] { options.outWidth, options.outHeight };
}
private static Bitmap decodeStream(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
BitmapFactory.Options options) {
if (options.inJustDecodeBounds) {
// This is large, but jpeg headers are not size bounded so we need something large enough to minimize
// the possibility of not being able to fit enough of the header in the buffer to get the image size so
// that we don't fail to load images. The BufferedInputStream will create a new buffer of 2x the
// original size each time we use up the buffer space without passing the mark so this is a maximum
// bound on the buffer size, not a default. Most of the time we won't go past our pre-allocated 16kb.
is.mark(MARK_POSITION);
} else {
// Once we've read the image header, we no longer need to allow the buffer to expand in size. To avoid
// unnecessary allocations reading image data, we fix the mark limit so that it is no larger than our
// current buffer size here. See issue #225.
bufferedStream.fixMarkLimit();
}
final Bitmap result = BitmapFactory.decodeStream(is, null, options);
return result;
// decode()方法执行后会返回一个Bitmap对象
// 此时图片已经被加载出来
// 接下来的工作是让加载了的Bitmap显示到界面上
// 请回到分析25
}
...
}
步骤3:返回图片资源
StreamBitmapDecoder
(分析25)-> ImageVideoBitmapDecoder
(分析24)-> GifBitmapWrapperResourceDecoder``decodeBitmapWrapper()
(分析23)decodeBitmapWrapper()
贴出<-- 分析23:decodeBitmapWrapper -->
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
// 将Resource
}
return result;
// 最终返回的是一个GifBitmapWrapper对象:既能封装GIF,又能封装Bitmap,从而保证了不管是什么类型的图片,Glide都能加载
// 接下来我们分析下GifBitmapWrapper() ->>分析27
}
<-- 分析27:GifBitmapWrapper() -->
// 作用:分别对gifResource和bitmapResource做了一层封装
public class GifBitmapWrapper {
private final Resource
private final Resource
public GifBitmapWrapper(Resource
if (bitmapResource != null && gifResource != null) {
throw new IllegalArgumentException("Can only contain either a bitmap resource or a gif resource, not both");
}
if (bitmapResource == null && gifResource == null) {
throw new IllegalArgumentException("Must contain either a bitmap resource or a gif resource");
}
this.bitmapResource = bitmapResource;
this.gifResource = gifResource;
}
/**
* Returns the size of the wrapped resource.
*/
public int getSize() {
if (bitmapResource != null) {
return bitmapResource.getSize();
} else {
return gifResource.getSize();
}
}
/**
* Returns the wrapped {@link Bitmap} resource if it exists, or null.
*/
public Resource
return bitmapResource;
}
/**
* Returns the wrapped {@link GifDrawable} resource if it exists, or null.
*/
public Resource
return gifResource;
}
}
GifBitmapWrapper
对象会一直向上返回GifBitmapWrapperResourceDecoder的decode()
时(分析20),会对GifBitmapWrapper
对象再做一次封装,如下所示:
此处将上面的分析20再次粘贴过来
<--分析20:GifBitmapWrapperResourceDecoder对象的decode() -->
public class GifBitmapWrapperResourceDecoder implements ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> {
...
@Override
public Resource
try {
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
// 直接看这里
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
// 将GifBitmapWrapper封装到一个GifBitmapWrapperResource对象中(Resource
// 该GifBitmapWrapperResource和上述的BitmapResource类似- 实现了Resource接口,可通过get()来获取封装的具体内容
// GifBitmapWrapperResource()源码分析 - >>分析28
}
<-- 分析28: GifBitmapWrapperResource()-->
// 作用:经过这层封装后,我们从网络上得到的图片就能够以Resource接口的形式返回,并且还能同时处理Bitmap图片和GIF图片这两种情况。
public class GifBitmapWrapperResource implements Resource<GifBitmapWrapper> {
private final GifBitmapWrapper data;
public GifBitmapWrapperResource(GifBitmapWrapper data) {
if (data == null) {
throw new NullPointerException("Data must not be null");
}
this.data = data;
}
@Override
public GifBitmapWrapper get() {
return data;
}
@Override
public int getSize() {
return data.getSize();
}
@Override
public void recycle() {
Resource
if (bitmapResource != null) {
bitmapResource.recycle();
}
Resource
if (gifDataResource != null) {
gifDataResource.recycle();
}
}
}DecodeJob
的decodeFromSourceData()
(分析19)中:<-- 分析19:decodeFromSourceData()() -->
private Resource<T> decodeFromSourceData(A data) throws IOException {
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
return decoded;
// 该方法返回的是一个`Resource<T>`对象,其实就是Resource<GifBitmapWrapper>对象
}
DecodeJob
的decodeFromSource()
中(分析15)<-- 分析15:DecodeJob的decodeFromSource() -->
class DecodeJob<A, T, Z> {
...
public Resource
Resource
// 返回到这里,最终得到了这个Resource
return transformEncodeAndTranscode(decoded);
// 作用:将该Resource
}
<--分析29:transformEncodeAndTranscode() -->
private Resource
Resource
// 把Resource
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from source", startTime);
}
return result;
}
<-- 分析30:transcode(transformed) -->
private Resource
if (transformed == null) {
return null;
}
return transcoder.transcode(transformed);
// 调用了transcoder的transcode()
// 这里的transcoder就是第二步load()中的GifBitmapWrapperDrawableTranscoder对象(回看下第2步生成对象的表)
// 接下来请看 ->>分析31
}
<-- 分析31:GifBitmapWrapperDrawableTranscoder.transcode(transformed) -->
// 作用:转码,即从Resource
// 因为GifBitmapWrapper是无法直接显示到ImageView上的,只有Bitmap或者Drawable才能显示到ImageView上。
public class GifBitmapWrapperDrawableTranscoder implements ResourceTranscoder<GifBitmapWrapper, GlideDrawable> {
...
@Override
public Resource
GifBitmapWrapper gifBitmap = toTranscode.get();
// 步骤1:从Resource
Resource
// 步骤2:从GifBitmapWrapper中取出Resource
final Resource extends GlideDrawable> result;
// 接下来做了一个判断:
// 1. 若Resource
if (bitmapResource != null) {
result = bitmapDrawableResourceTranscoder.transcode(bitmapResource);
// 则需要再做一次转码:将Bitmap转换成Drawable对象
// 因为要保证静图和动图的类型一致性,否则难以处理->>分析32
} else {
// 2. 若Resource
// 那么直接调用getGifResource()方法将图片取出
// 因为Glide用于加载GIF图片是使用的GifDrawable这个类,它本身就是一个Drawable对象
result = gifBitmap.getGifResource();
}
return (Resource
}
...
}
<-- 分析32:bitmapDrawableResourceTranscoder.transcode(bitmapResource)-->
// 作用:再做一次转码:将Bitmap转换成Drawable对象
public class GlideBitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, GlideBitmapDrawable> {
...
@Override
public Resource
GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
// 创建GlideBitmapDrawable对象,并把Bitmap封装到里面
return new GlideBitmapDrawableResource(drawable, bitmapPool);
// 对GlideBitmapDrawable再进行一次封装,返回Resource
}
}
Resource
对象,还是动图的Resource
对象,它们都属于父类Resource
对象transcode()
返回的是Resource
对象,即转换过后的Resource
DecodeJob的decodeFromSource()
中,得到的Resource对象 是 Resource
对象
步骤4:在主线程显示图片
EngineRunnable
的 run()
中(分析12)
<--分析12:EngineRunnable的run() -->
@Override
public void run() {
try {
resource = decode();
// 最终得到了Resource
// 接下来的工作:将该图片显示出来
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
// 表示图片加载已经完成 ->>分析33
}
}
<-- 分析33: onLoadComplete(resource) -->
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
// 该manager即EngineJob对象
// 实际上调用的是EngineJob的onResourceReady() - >>分析34
}
<-- 分析34:EngineJob的onResourceReady() : -->
class EngineJob implements EngineRunnable.EngineRunnableManager {
...
private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback());
// 创建线程,并绑定主线程的Looper
private final List
@Override
public void onResourceReady(final Resource> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
// 使用Handler发出一条 MSG_COMPLETE 消息
// 那么在MainThreadCallback的handleMessage()方法中就会收到这条消息 ->>分析35
// 从此处开始,所有逻辑又回到主线程中进行了,即更新UI
}
<-- 分析35:MainThreadCallback的handleMessage()-->
private static class MainThreadCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message message) {
if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
EngineJob job = (EngineJob) message.obj;
if (MSG_COMPLETE == message.what) {
job.handleResultOnMainThread();
// 调用 EngineJob的handleResultOnMainThread() ->>分析36
} else {
job.handleExceptionOnMainThread();
}
return true;
}
return false;
}
}
...
}
<-- 分析36:handleResultOnMainThread() -->
private void handleResultOnMainThread() {
// 通过循环,调用了所有ResourceCallback的onResourceReady()
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
// ResourceCallback 是在addCallback()方法当中添加的->>分析37
}
}
engineResource.release();
}
<-- 分析37:addCallback() -->
//
public void addCallback(ResourceCallback cb) {
Util.assertMainThread();
if (hasResource) {
cb.onResourceReady(engineResource);
// 会向cbs集合中去添加ResourceCallback
} else if (hasException) {
cb.onException(exception);
} else {
cbs.add(cb);
}
}
// 而addCallback()是在分析11:Engine的load()中调用的:
<-- 上面的分析11:Engine的load() -->
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public
DataLoadProvider
boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
engineJob.addCallback(cb);
// 调用addCallback()注册了一个ResourceCallback
// 上述参数cb是load()传入的的最后一个参数
// 而load()是在GenericRequest的onSizeReady()调用的->>回到分析9(下面重新贴多了一次)
return new LoadStatus(cb, engineJob);
}
...
}
<-- 上面的分析9:onSizeReady() -->
public void onSizeReady(int width, int height) {
...
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
// load()最后一个参数是this
// 所以,ResourceCallback类型参数cb是this
// 而GenericRequest本身实现了ResourceCallback接口
// 因此,EngineJob的回调 = cb.onResourceReady(engineResource) = 最终回调GenericRequest的onResourceReady() -->>分析6
}
}
<-- 分析38:GenericRequest的onResourceReady() -->
// onResourceReady()存在两个方法重载
// 重载1
public void onResourceReady(Resource> resource) {
Object received = resource.get();
// 获取封装的图片对象(GlideBitmapDrawable对象 或 GifDrawable对象
onResourceReady(resource, (R) received);
// 然后将该获得的图片对象传入到了onResourceReady()的重载方法中 ->>看重载2
}
// 重载2
private void onResourceReady(Resource> resource, R result) {
...
target.onResourceReady(result, animation);
// Target是在第3步into()的最后1行调用glide.buildImageViewTarget()方法来构建出的Target:GlideDrawableImageViewTarget对象
// ->>分析39
}
<-- 分析39:GlideDrawableImageViewTarget.onResourceReady -->
public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> {
@Override
public void onResourceReady(GlideDrawable resource, GlideAnimation super GlideDrawable> animation) {
if (!resource.isAnimated()) {
float viewRatio = view.getWidth() / (float) view.getHeight();
float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
&& Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
resource = new SquaringDrawable(resource, view.getWidth());
}
}
super.onResourceReady(resource, animation);
// 若是静态图片,就调用父类的.onResourceReady() 将GlideDrawable显示到ImageView上
// GlideDrawableImageViewTarget的父类是ImageViewTarget ->>分析40
this.resource = resource;
resource.setLoopCount(maxLoopCount);
resource.start();
// 如果是GIF图片,就调用resource.start()方法开始播放图片
}
@Override
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}
...
}
<-- 分析40:ImageViewTarget.onResourceReady() -->
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
...
@Override
public void onResourceReady(Z resource, GlideAnimation super Z> glideAnimation) {
if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
setResource(resource);
// 继续往下看
}
}
protected abstract void setResource(Z resource);
// setResource()是一个抽象方法
// 需要在子类具体实现:请回看上面分析39子类GlideDrawableImageViewTarget类重写的setResource():调用view.setImageDrawable(),而这个view就是ImageView
// 即setResource()的具体实现是调用ImageView的setImageDrawable() 并 传入图片,于是就实现了图片显示。
}总结
Glide
的基本功能 图片加载的全功能 解析完毕。
5. 总结
Glide
的基本功能 图片加载的全过程
Glide
的其他功能进行源码分析 ,有兴趣可以继续关注Carson_Ho的安卓开发笔记
帮顶 / 评论点赞!因为你的鼓励是我写作的最大动力!