到这里我们就完完整整的创建了一个request
,接下开我们就看看如何运行request
的:
RequestTracker#runRequest
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
可以看见我这里先判断有没有暂停,如果暂停了的话就加入暂停的队列里,没有的话就掉用request.begin()
方法。
继续查看:
GenericRequest#begin():
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
首先我们 的知道所谓的model
其实就是URL,如果穿入的是为空的话我们就调用onException(null);
方法,我们接着查看这个方法:
private void setErrorPlaceholder(Exception e) {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = model == null ? getFallbackDrawable() : null;
if (error == null) {
error = getErrorDrawable();
}
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(e, error);
}
可以看见我们会去尝试获得一个的错误图片,如果没有就是占位的图片,最后调用target.onLoadFailed
方法:
ImageViewTarget#onLoadFailed:
@Override
public void onLoadStarted(Drawable placeholder) {
view.setImageDrawable(placeholder);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
view.setImageDrawable(errorDrawable);
}
可以错误就直接设定错误图了。 然后看见开始的时候我们会调用占位图,这个在我们的begin
方法时候就调用onLoadStarted
方法了,这里错误就算差不多讲完了,我们回到begin
,为了很清楚我还贴了下来:
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
- 如果我们调用API指定了宽高那么
onSizeReady(overrideWidth, overrideHeight);
方法来给图片设定一个固定的宽高 - 如果没有我们就会调用
target.getSize(this);
方法来根据空间的宽高指定图片应有的宽高,这里不做具体分析我们知道最后调用的是onSizeReady方法
就行了。
GenericRequest #onSizeReady:
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
我们查看loadProvider.getModelLoader();
是不是之前貌似在哪遇到的?没错就是在loadGeneric
方法中构建的两个下载者,那loadProvider
又是什么呢?其实在我们创建DrawableTypeRequest
的时候我们忽略了一点~
DrawableTypeRequest :
public class DrawableTypeRequest<ModelType> extends DrawableRequestBuilder<ModelType> implements DownloadOptions {
private final ModelLoader<ModelType, InputStream> streamModelLoader;
private final ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader;
private final RequestManager.OptionsApplier optionsApplier;
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
...
}
- 查看构造器,我们是调用了
buildProvider
方法, 传入了两个下载器的引用,在buildProvider
方法中我们看见在调用了glide.buildTranscoder()
方法来构建一个ResourceTranscoder
,看见trans
??? ,所以他是对图像进行转码的,这里实际会构造出这里实际会构建出一个GifBitmapWrapperDrawableTranscoder
对象。 - 接下来调用了glide.buildDataProvider()方法来构建一个
DataLoadProvider
,它是用于对图片进行编解码的,由于DataLoadProvider是一个接口,这里实际会构建出一个ImageVideoGifDrawableLoadProvider对象。 - new了一个ImageVideoModelLoader的实例,并把之前loadGeneric()方法中构建的两个ModelLoader封装到了
ImageVideoModelLoader
当中。 - 到最后把上面三放到
FixedLoadProvider
中,而激动人心的是FixedLoadProvider
就是我们上文onSizeReady()方法中的loadProvider了。
回到onSizeReady
方法中,我们拿到了ResourceTranscoder
和ImageVideoModelLoader
然后调用ResourceTranscoder
得到ResourceFetcher()
比较陌生那他什么呢? 我们打开源码瞧一瞧:
public class ImageVideoModelLoader<A> implements ModelLoader<A, ImageVideoWrapper> {
private static final String TAG = "IVML";
private final ModelLoader<A, InputStream> streamLoader;
private final ModelLoader<A, ParcelFileDescriptor> fileDescriptorLoader;
public ImageVideoModelLoader(ModelLoader<A, InputStream> streamLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorLoader) {
if (streamLoader == null && fileDescriptorLoader == null) {
throw new NullPointerException("At least one of streamLoader and fileDescriptorLoader must be non null");
}
this.streamLoader = streamLoader;
this.fileDescriptorLoader = fileDescriptorLoader;
}
@Override
public DataFetcher<ImageVideoWrapper> getResourceFetcher(A model, int width, int height) {
DataFetcher<InputStream> streamFetcher = null;
if (streamLoader != null) {
// 1~
streamFetcher = streamLoader.getResourceFetcher(model, width, height);
}
DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher = null;
if (fileDescriptorLoader != null) {
fileDescriptorFetcher = fileDescriptorLoader.getResourceFetcher(model, width, height);
}
if (streamFetcher != null || fileDescriptorFetcher != null) {
// 2~
return new ImageVideoFetcher(streamFetcher, fileDescriptorFetcher);
} else {
return null;
}
}
static class ImageVideoFetcher implements DataFetcher<ImageVideoWrapper> {
private final DataFetcher<InputStream> streamFetcher;
private final DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher;
public ImageVideoFetcher(DataFetcher<InputStream> streamFetcher,
DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher) {
this.streamFetcher = streamFetcher;
this.fileDescriptorFetcher = fileDescriptorFetcher;
}
...
}
}
我们看见一那里 1 那里获得一个DataFetcher
,这个streamLoader
是在loadGeneric()
方法中构建出的StreamStringLoader
对象,的getResourceFetcher()
方法会得到一个HttpUrlFetcher
对象,在 2~ 那里会返回一个ImageVideoFetcher
对象,现在我们返回onSizeReady
方法中;
GenericRequest #onSizeReady:
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
/ 3~
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
继续在 3~ 中我们调用了engine.load
方法 ,并且传入上面的ImageVideoFetcher、GifBitmapWrapper.DrawableTranscoder等等一系列的值
engine#load
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
// EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
// 4~
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
// 5~
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
...
}
这些代码主要是处理缓存的,所以我们冲 4~ 开始看 根据key创建EngineJob
他主要的作用是用来开启线程的,后面异步加载需要,接着看 5~ 这里后面讲,继续看下面,EngineRunnable
创建传入了上两个engine
的引用,并在下面调用
engineJob.start(runnable);
开始了运行run
方法:
EngineRunnable#run()
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
//6~
resource = decode();
} 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);
}
}
6~ 这里调用了decode ()
:
rivate Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
这里看是不是决定用缓存还是根据资源获取,因为走线我们就只看decodeFromSource();
:
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
这里我们看见返回decodeJob
调用decodeFromSource();
的方法:
class DecodeJob<A, T, Z> {
...
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();‘
/6~
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
//7~
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
...
}
首先我们看见 6~ 中的 fetcher
其实 是在onSizeReady()
方法中得到的ImageVideoFetcher
对象,接着看loadData
方法:
ImageVideoFetcher# loadData:
@Override
public ImageVideoWrapper loadData(Priority priority) throws Exception {
InputStream is = null;
if (streamFetcher != null) {
try {
//7~
is = streamFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);
}
if (fileDescriptorFetcher == null) {
throw e;
}
}
}
ParcelFileDescriptor fileDescriptor = null;
if (fileDescriptorFetcher != null) {
try {
fileDescriptor = fileDescriptorFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);
}
if (is == null) {
throw e;
}
}
}
return new ImageVideoWrapper(is, fileDescriptor);
}
7~ 中我们发现他实际是调用streamFetcher.loadData(priority);
方法,这个streamFetcher
就是我们之前创建的HttpUrlFetcher
,所以无聊的继续:
HttpUrlFetcher # loadData()
public class HttpUrlFetcher implements DataFetcher<InputStream> {
...
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new IOException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(2500);
urlConnection.setReadTimeout(2500);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else {
if (statusCode == -1) {
throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
}
throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
}
}
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}
...
}
说实话看见urlConnection
的时候我真的哭了,你终于开始请求数据了 ,不过这里主要是urlConnection
相关的网络请求这里不多讲,但是我们需要知道理解的是返回了一个InputStream
对象就行了。接着回到loadData()
中我们可以看到他会将我们的InputStream
和fileDescriptor
打包成ImageVideoWrapper
传回DecodeJob
对象的decodeSource
方法中,继续执行: 7~ 调用了decodeFromSourceData
方法 ,将返回的ImageVideoWrapper
作为参数穿进去:
decodeJob #decodeFromSourceData方法:
private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
// 8~
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded from source", startTime);
}
}
return decoded;
}
这里会执行 8~ 调用loadProvider.getSourceDecoder().decode(data, width, height);
这里的loadProvider
就是FixedLoadProvider
也就是我们上文onSizeReady()方法中的loadProvider了这里再次出现,而.getSourceDecoder()
得到的就是GifBitmapWrapperResourceDecoder
对象,而就是这个对象调用了decode
方法进行解码操作:
GifBitmapWrapperResourceDecoder#decode
public class GifBitmapWrapperResourceDecoder implements ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> {
...
@SuppressWarnings("resource")
// @see ResourceDecoder.decode
@Override
public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();
GifBitmapWrapper wrapper = null;
try {
//9~
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
}
private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException {
final GifBitmapWrapper result;
if (source.getStream() != null) {
//10~
result = decodeStream(source, width, height, bytes);
} else {
result = decodeBitmapWrapper(source, width, height);
}
return result;
}
private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes)
throws IOException {
InputStream bis = streamFactory.build(source.getStream(), bytes);
bis.mark(MARK_LIMIT_BYTES);
ImageHeaderParser.ImageType type = parser.parse(bis);
bis.reset();
GifBitmapWrapper result = null;
if (type == ImageHeaderParser.ImageType.GIF) {
result = decodeGifWrapper(bis, width, height);
}
// Decoding the gif may fail even if the type matches.
if (result == null) {
// We can only reset the buffered InputStream, so to start from the beginning of the stream, we need to
// pass in a new source containing the buffered stream rather than the original stream.
ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());
result = decodeBitmapWrapper(forBitmapDecoder, width, height);
}
return result;
}
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
//11~
Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height);
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
}
return result;
}
...
9~中调用了 decode
的重载方法 接着在重载方法里 10~ 调用 decodeStream ()
方法从服务器里读取数据流,来到decodeStream
方法,会调用parser.parse(bis)
,在.parse
方法里从流中到了两个字节的数据,然后判断数据数据类型是GIF和还是静态图,从而decodeGifWrapper
方法,还是decodeBitmapWrapper
方法,由于decodeGifWrapper 超出本人能力范围,
,我们就看看decodeBitmapWrapper
,也就是静态图片的解决方案好了。
11~ 会调用了bitmapDecoder.decode()
方法,这个bitmapDecoder
是ImageVideoBitmapDecoder
对象:
ImageVideoBitmapDecoder#decode
public class ImageVideoBitmapDecoder implements ResourceDecoder<ImageVideoWrapper, Bitmap> {
private final ResourceDecoder<InputStream, Bitmap> streamDecoder;
private final ResourceDecoder<ParcelFileDescriptor, Bitmap> fileDescriptorDecoder;
public ImageVideoBitmapDecoder(ResourceDecoder<InputStream, Bitmap> streamDecoder,
ResourceDecoder<ParcelFileDescriptor, Bitmap> fileDescriptorDecoder) {
this.streamDecoder = streamDecoder;
this.fileDescriptorDecoder = fileDescriptorDecoder;
}
@Override
public Resource<Bitmap> decode(ImageVideoWrapper source, int width, int height) throws IOException {
Resource<Bitmap> result = null;
//12~
InputStream is = source.getStream();
if (is != null) {
try {
result = streamDecoder.decode(is, width, height);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to load image from stream, trying FileDescriptor", e);
}
}
}
if (result == null) {
ParcelFileDescriptor fileDescriptor = source.getFileDescriptor();
if (fileDescriptor != null) {
result = fileDescriptorDecoder.decode(fileDescriptor, width, height);
}
}
return result;
}
...
}
12~调用source.getStream();
方法,得到InputStream
也就是服务器传回的流,接着用streamDecoder.decode()
方法进行解码,这个streamDecoder
就是StreamBitmapDecoder
:
StreamBitmapDecoder#decode():
public class StreamBitmapDecoder implements ResourceDecoder<InputStream, Bitmap> {
...
private final Downsampler downsampler;
private BitmapPool bitmapPool;
private DecodeFormat decodeFormat;
public StreamBitmapDecoder(Downsampler downsampler, BitmapPool bitmapPool, DecodeFormat decodeFormat) {
this.downsampler = downsampler;
this.bitmapPool = bitmapPool;
this.decodeFormat = decodeFormat;
}
@Override
public Resource<Bitmap> decode(InputStream source, int width, int height) {
//13~
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
return BitmapResource.obtain(bitmap, bitmapPool);
}
...
13~ 看见这里让人欣喜传入的InputStream
最终返回了Bitmap
说明这行代码里包括了真真正正的解析代码:
Downsampler #decode
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);
try {
if (options.inJustDecodeBounds) {
is.reset();
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.ERROR)) {
Log.e(TAG, "Exception loading inDecodeBounds=" + options.inJustDecodeBounds
+ " sample=" + options.inSampleSize, e);
}
}
return result;
}
...
}
过长的篇幅确实让人充满睡意,Glide真是一场拉锯战。。
上面得篇幅不做解析,我们只需要知道他把流进行解析成Bitmap即可,当然其中还包括我们设定的对图片的压缩,甚至还有旋转、圆角等逻辑处理包扩在内。回到
StreamBitmapDecoder#decode():中我们调用了BitmapResource.obtain(bitmap, bitmapPool);
将Bitmap
包装成Resource<Bitmap>对象:
public class BitmapResource implements Resource<Bitmap> {
private final Bitmap bitmap;
private final BitmapPool bitmapPool;
/**
* Returns a new {@link BitmapResource} wrapping the given {@link Bitmap} if the Bitmap is non-null or null if the
* given Bitmap is null.
*
* @param bitmap A Bitmap.
* @param bitmapPool A non-null {@link BitmapPool}.
*/
public static BitmapResource obtain(Bitmap bitmap, BitmapPool bitmapPool) {
if (bitmap == null) {
return null;
} else {
return new BitmapResource(bitmap, bitmapPool);
}
}
public BitmapResource(Bitmap bitmap, BitmapPool bitmapPool) {
if (bitmap == null) {
throw new NullPointerException("Bitmap must not be null");
}
if (bitmapPool == null) {
throw new NullPointerException("BitmapPool must not be null");
}
this.bitmap = bitmap;
this.bitmapPool = bitmapPool;
}
@Override
public Bitmap get() {
return bitmap;
}
@Override
public int getSize() {
return Util.getBitmapByteSize(bitmap);
}
@Override
public void recycle() {
if (!bitmapPool.put(bitmap)) {
bitmap.recycle();
}
}
}
这个源码太简单我们知道是个Bitmap
的包装类就行了,然后我们就一层一层的返回,StreamBitmapDecoder
会将值返回到ImageVideoBitmapDecoder
当中,而ImageVideoBitmapDecoder
又会将值返回到GifBitmapWrapperResourceDecoder
的decodeBitmapWrapper()方法当中。到这里停一停看看下面的工作:
GifBitmapWrapperResourceDecoder #decodeBitmapWrapper
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height);
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
}
return result;
他会把返回的Resource<Bitmap>
又包装进GifBitmapWrapper
中 所以接下看看GifBitmapWrapper
代码吧:
public class GifBitmapWrapper {
private final Resource<GifDrawable> gifResource;
private final Resource<Bitmap> bitmapResource;
public GifBitmapWrapper(Resource<Bitmap> bitmapResource, Resource<GifDrawable> gifResource) {
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<Bitmap> getBitmapResource() {
return bitmapResource;
}
/**
* Returns the wrapped {@link GifDrawable} resource if it exists, or null.
*/
public Resource<GifDrawable> getGifResource() {
return gifResource;
}
}
没什么技术含量只是做了进一步封装而已,然后又继续向上返回,,,最后返回到GifBitmapWrapperResourceDecoder
对象中:
GifBitmapWrapperResourceDecoder# decode
@Override
public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();
GifBitmapWrapper wrapper = null;
try {
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
//14~
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
}
14~中又会包装一次Resource<GifBitmapWrapper>
对象返回,他和前文的BitmapResource
很相似,都实现了Resource
接口:
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<Bitmap> bitmapResource = data.getBitmapResource();
if (bitmapResource != null) {
bitmapResource.recycle();
}
Resource<GifDrawable> gifDataResource = data.getGifResource();
if (gifDataResource != null) {
gifDataResource.recycle();
}
}
}
这么多次的封装对象实际就是为了让他即实现Resource
接口,同时又能包裹Bitmap图片和GIF图片。然后返回到DecodeJob
的decodeFromSourceData()
中,然后又回到decodeSource()
方法中,然后回到decodeFromSource()
方法中:
DecodeJob#decodeFromSource
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
可以看见调用了 transformEncodeAndTranscode
方法:
DecodeJob#transformEncodeAndTranscode:
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
long startTime = LogTime.getLogTime();
Resource<T> transformed = transform(decoded);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transformed resource from source", startTime);
}
writeTransformedToCache(transformed);
startTime = LogTime.getLogTime();
//15~
Resource<Z> result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from source", startTime);
}
return result;
}
private Resource<Z> transcode(Resource<T> transformed) {
if (transformed == null) {
return null;
}
return transcoder.transcode(transformed);
}
其他不用管都是关于缓存的,我们看 15~ Resource<T>对象转换成Resource<Z>对象了,而方法里调用transcoder.transcode
方法,这个transcoder
方法还记得吗?我们FixedLoadProvider
里包裹的其中者之一,然后在onSizeReady()方法中传入了这个参数,并把它交给了Engine
,Engine
又给了DecodeJob
中,所以他就是GifBitmapWrapperDrawableTranscoder
对象:
GifBitmapWrapperDrawableTranscoder# transcode:
public class GifBitmapWrapperDrawableTranscoder implements ResourceTranscoder<GifBitmapWrapper, GlideDrawable> {
private final ResourceTranscoder<Bitmap, GlideBitmapDrawable> bitmapDrawableResourceTranscoder;
public GifBitmapWrapperDrawableTranscoder(
ResourceTranscoder<Bitmap, GlideBitmapDrawable> bitmapDrawableResourceTranscoder) {
this.bitmapDrawableResourceTranscoder = bitmapDrawableResourceTranscoder;
}
@Override
public Resource<GlideDrawable> transcode(Resource<GifBitmapWrapper> toTranscode) {
GifBitmapWrapper gifBitmap = toTranscode.get();
Resource<Bitmap> bitmapResource = gifBitmap.getBitmapResource();
final Resource<? extends GlideDrawable> result;
if (bitmapResource != null) {
//16~
result = bitmapDrawableResourceTranscoder.transcode(bitmapResource);
} else {
result = gifBitmap.getGifResource();
}
return (Resource<GlideDrawable>) result;
}
...
}
我们先说明一下transcode
就是转码的意思,因为我们知道包装这么多GifBitmapWrapper
是无法显示的,所以我们要做的事把包裹的“蚕茧”一层一层的撕掉,都出原有的样子然后放在所谓的Target
显示出来,先得到GifBitmapWrapper
-》Resource<Bitmap>
,这时候我们判断Resource<Bitmap>
对象是不是为空,如果是就代表他是GIF图,直接调用getGifResource()
得到图片即可(因为GIF本身就是Drawable
对象),如果是Resource<Bitmap>
我们得还做一次转码将Bitmap转换成Drawable对象才行:在16~中:
GlideBitmapDrawableTranscoder#transcode()
public class GlideBitmapDrawableTranscoder implements ResourceTranscoder<Bitmap, GlideBitmapDrawable> {
private final Resources resources;
private final BitmapPool bitmapPool;
public GlideBitmapDrawableTranscoder(Context context) {
this(context.getResources(), Glide.get(context).getBitmapPool());
}
public GlideBitmapDrawableTranscoder(Resources resources, BitmapPool bitmapPool) {
this.resources = resources;
this.bitmapPool = bitmapPool;
}
@Override
public Resource<GlideBitmapDrawable> transcode(Resource<Bitmap> toTranscode) {
GlideBitmapDrawable drawable = new GlideBitmapDrawable(resources, toTranscode.get());
return new GlideBitmapDrawableResource(drawable, bitmapPool);
}
...
这里new出了一个GlideBitmapDrawable
对象 并把Bitmap
封装到里面。然后对GlideBitmapDrawable
再进行一次封装
返回一个Resource<GlideBitmapDrawable>
对象。 因为GlideBitmapDrawable
和GifDrawable
他们的父类都是GlideDrawable
对象,所以都OK,然后返回向上直接转型Resource<GlideDrawable>
,而前文所谓的Resource<Z>
就是他。接着回到DecodeJob#decodeFromSource
方法继续返回 -> EngineRunnable#decodeFromCache
-> EngineRunnable#decode()
->EngineRunnable#run()
中:
EngineRunnable#decode()
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode();
} 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 {
//17~
onLoadComplete(resource);
}
}
调用回调:17~:
EngineRunnable#onLoadComplete():
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}
这个manager
就是EngineJob
对象:
EngineJob#onResourceReady():
class EngineJob implements EngineRunnable.EngineRunnableManager {
private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback());
private final List<ResourceCallback> cbs = new ArrayList<ResourceCallback>();
...
public void addCallback(ResourceCallback cb) {
Util.assertMainThread();
if (hasResource) {
//21~
cb.onResourceReady(engineResource);
} else if (hasException) {
cb.onException(exception);
} else {
cbs.add(cb);
}
}
@Override
public void onResourceReady(final Resource<?> resource) {
this.resource = resource;
//18~
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
private void handleResultOnMainThread() {
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
engineResource.release();
}
@Override
public void onException(final Exception e) {
this.exception = e;
MAIN_THREAD_HANDLER.obtainMessage(MSG_EXCEPTION, this).sendToTarget();
}
private void handleExceptionOnMainThread() {
if (isCancelled) {
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received an exception without any callbacks to notify");
}
hasException = true;
listener.onEngineJobComplete(key, null);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
cb.onException(exception);
}
}
}
//19~
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) {
//20~
job.handleResultOnMainThread();
} else {
job.handleExceptionOnMainThread();
}
return true;
}
return false;
}
}
...
}
18~中我们创建的是一个MSG发送的主线程,然后把自己导入进这个MSG中, 19~ 所以接下来的在MainThreadCallback
中的handleMessage
会在主线程中执行: 20~看见会调用job.handleResultOnMainThread();
:我们看见这个方法只是在遍历寻找回调,而在之前Engine
的load
方法中我们早一注册了接口,这个回调真是GenericRequest
自身,所以最终对调用到 21~哪一行也就是 cb.onResourceReady(engineResource);
实际就是 GenericRequest
的:
GenericRequest#onResourceReady
public void onResourceReady(Resource<?> resource) {
if (resource == null) {
onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
+ " inside, but instead got null."));
return;
}
//22~
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
onException(new Exception("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.")
));
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't set the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady(resource, (R) received);
}
private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
+ (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
}
这里有两个onResourceReady
,其中只有一个参数的是我们调用的, 22~ 中我们得到的received
不是GlideBitmapDrawable
就是GifDrawable
,然后调用含有两个参数的重载方法:onResourceReady
:
- 设置标志完成
- 创建动画对象
- 调用
target.onResourceReady
前文我们知道target
就是GlideDrawableImageViewTarget
GlideDrawableImageViewTarget# onResourceReady
public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> {
private static final float SQUARE_RATIO_MARGIN = 0.05f;
private int maxLoopCount;
private GlideDrawable resource;
public GlideDrawableImageViewTarget(ImageView view) {
this(view, GlideDrawable.LOOP_FOREVER);
}
public GlideDrawableImageViewTarget(ImageView view, int maxLoopCount) {
super(view);
this.maxLoopCount = maxLoopCount;
}
@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);
this.resource = resource;
resource.setLoopCount(maxLoopCount);
resource.start();
}
@Override
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}
@Override
public void onStart() {
if (resource != null) {
resource.start();
}
}
@Override
public void onStop() {
if (resource != null) {
resource.stop();
}
}
这里除了动画以外貌似没有好处理的所以我们得不目光一道父类即super.onResourceReady(resource, animation);
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)) {
//23~
setResource(resource);
}
}
protected abstract void setResource(Z resource);
23~ 我们可以看到调用了抽象方法setResource
:所以他应该在子类也就是GlideDrawableImageViewTarget
调用的setResource
方法
GlideDrawableImageViewTarget#setResource
@Override
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}