glide是谷歌推荐的Android图片加载框架,其优秀的缓存策略、Activity的生命周期的继承、GIF图片的支持等都是为人所称道的地方。下面是用glide加载一张图片的调用。
private void loadImage() {
Glide.with(this)
.load("http://pic2.orsoon.com/2017/0118/20170118011032176.jpg")
.into(ivTest);
}
那么,该框架是如何实际运作的呢,我会通过“Glide之旅”系列博客尽可能详细地将我的心得记录下来。“Glide之旅”系列文章汇总:
Glide之旅 —— Registry
Glide之旅 —— DecodeJob
EngineJob(com.bumptech.glide.load.engine.DecodeJob)是整个glide开始进行图片编解码、缓存等一系列的动作的发动机。
首先明确一点,在DecodeJob中初始化变量的值是来源于 com.bumptech.glide.request.SingleRequest:
package com.bumptech.glide.request;
...
public final class SingleRequest<R> implements Request,
SizeReadyCallback,
ResourceCallback,
FactoryPools.Poolable {
...
@Override
public void onSizeReady(int width, int height) {
...
loadStatus = engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
...
}
...
}
先来看看执行解码操作的入口函数:
package com.bumptech.glide.load.engine;
...
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
Runnable, Comparable>, Poolable {
...
@Override
public void run() {
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (RuntimeException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "DecodeJob threw unexpectedly"
+ ", isCancelled: " + isCancelled
+ ", stage: " + stage, e);
}
if (stage != Stage.ENCODE) {
notifyFailed();
}
if (!isCancelled) {
throw e;
}
}
}
...
}
也就是说,一般情况下,直接跳转到runWrapped()中去:
package com.bumptech.glide.load.engine;
...
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
Runnable, Comparable>, Poolable {
...
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
...
}
runReason在初始状态下设定为INITIALIZE,这个时候再看一下getNextGenerator(Stage.INITIALIZE)和getNextGenerator():
package com.bumptech.glide.load.engine;
...
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
Runnable, Comparable>, Poolable {
...
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
...
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
...
}
由于默认情况下,diskCacheStrategy的值为DiskCacheStrategy.AUTOMATIC:
package com.bumptech.glide.load.engine;
...
public abstract class DiskCacheStrategy {
...
public static final DiskCacheStrategy AUTOMATIC = new DiskCacheStrategy() {
@Override
public boolean isDataCacheable(DataSource dataSource) {
return dataSource == DataSource.REMOTE;
}
@Override
public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
EncodeStrategy encodeStrategy) {
return ((isFromAlternateCacheKey && dataSource == DataSource.DATA_DISK_CACHE)
|| dataSource == DataSource.LOCAL)
&& encodeStrategy == EncodeStrategy.TRANSFORMED;
}
@Override
public boolean decodeCachedResource() {
return true;
}
@Override
public boolean decodeCachedData() {
return true;
}
};
...
}
由上面可以分析出,在runReason为INITIALIZE的情况下,stage此时为RESOURCE_CACHE,currentGenerator为ResourceCacheGenerator的实例,再回到runWrapped()中去,接下来调用runGenerators():
package com.bumptech.glide.load.engine;
...
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
Runnable, Comparable>, Poolable {
...
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
...
}
从这个函数源码看出,完整执行的情况下,会依次调用ResourceCacheGenerator、DataCacheGenerator和SourceGenerator中的startNext(),接下来,分别来看看三个DataFetcherGenerator的实现类是怎么进行的:
package com.bumptech.glide.load.engine;
...
class ResourceCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback
很明显,由于是请求一张之前从未被加载过的图片,那么在ROM缓存中是不可能找到对应的缓存文件的,所以最终会因为
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
而返回false。
package com.bumptech.glide.load.engine;
...
class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback
这里也需要先取得ROM缓存文件,故和ResourceCacheGenerator的结果一样。
package com.bumptech.glide.load.engine;
...
class SourceGenerator implements DataFetcherGenerator,
DataFetcher.DataCallback
刚开始执行时,dataToCache必定为null,sourceCacheGenerator也一样,这个时候根据注册的ModelLoader实例创建的LoadData实例的DataFetcher来进行加载远程图片的原始数据,结合注册项可知,执行HttpUrlFetcher中的loadData方法:
package com.bumptech.glide.load.data;
...
public class HttpUrlFetcher implements DataFetcher<InputStream> {
...
@Override
public void loadData(Priority priority, DataCallback super InputStream> callback) {
long startTime = LogTime.getLogTime();
final InputStream result;
try {
result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,
glideUrl.getHeaders());
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
return;
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)
+ " ms and loaded " + result);
}
callback.onDataReady(result);
}
...
}
在这里面通过网络请求得到图片的原始数据的流InputStream(如果请求顺利的话),然后再回调SourceGenerator中的onDataReady方法:
package com.bumptech.glide.load.engine;
...
class SourceGenerator implements DataFetcherGenerator,
DataFetcher.DataCallback
这样一来,就通过dataToCache将图片的原始数据进行临时存储,然后执行reschedule(),即跳转到DecodeJob中去:
package com.bumptech.glide.load.engine;
...
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
Runnable, Comparable>, Poolable {
...
@Override
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
...
}
继续跳转到EngineJob中的reschedule方法中去:
package com.bumptech.glide.load.engine;
...
class EngineJob implements DecodeJob.Callback, Poolable {
...
@Override
public void reschedule(DecodeJob> job) {
if (isCancelled) {
MAIN_THREAD_HANDLER.obtainMessage(MSG_CANCELLED, this).sendToTarget();
} else {
getActiveSourceExecutor().execute(job);
}
}
...
}
接下来,就是切换线程进行操作,继续从DecodeJob的入口函数进行工作,只是,这次runReason的值换成了SWITCH_TO_SOURCE_SERVICE,运行到runGenerators(),此时的currentGenerator的实例是SourceGenerator,那么执行到其中的startNext()的时候,由于现在的dataToCache已经存放了请求图片的原始数据,所以进行文件缓存:
package com.bumptech.glide.load.engine;
...
class SourceGenerator implements DataFetcherGenerator,
DataFetcher.DataCallback
在helper.getDiskCache().put(originalKey, writer)中显然是做缓存了:
package com.bumptech.glide.load.engine.cache;
...
public class DiskLruCacheWrapper implements DiskCache {
...
@Override
public void put(Key key, Writer writer) {
writeLocker.acquire(key);
try {
String safeKey = safeKeyGenerator.getSafeKey(key);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Put: Obtained: " + safeKey + " for for Key: " + key);
}
try {
DiskLruCache diskCache = getDiskCache();
Value current = diskCache.get(safeKey);
if (current != null) {
return;
}
DiskLruCache.Editor editor = diskCache.edit(safeKey);
if (editor == null) {
throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
}
try {
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Unable to put to disk cache", e);
}
}
} finally {
writeLocker.release(key);
}
}
...
}
再看writer.write(file)的写入文件操作:
package com.bumptech.glide.load.model;
...
public class StreamEncoder implements Encoder<InputStream> {
private static final String TAG = "StreamEncoder";
private final ArrayPool byteArrayPool;
public StreamEncoder(ArrayPool byteArrayPool) {
this.byteArrayPool = byteArrayPool;
}
@Override
public boolean encode(InputStream data, File file, Options options) {
byte[] buffer = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
boolean success = false;
OutputStream os = null;
try {
os = new FileOutputStream(file);
int read;
while ((read = data.read(buffer)) != -1) {
os.write(buffer, 0, read);
}
os.close();
success = true;
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to encode data onto the OutputStream", e);
}
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// Do nothing.
}
}
byteArrayPool.put(buffer, byte[].class);
}
return success;
}
}
注意到,这里是将图片的原始数据流写入到了相应的缓存文件中去,那么特定规格(指定宽高值和色彩模式)的图片缓存文件呢?别急,在SourceGenerator的cacheData方法最后给sourceCacheGenerator创建了一个实例,也就是说,还得执行DataCacheGenerator中的startNext():
package com.bumptech.glide.load.engine;
...
class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback {
...
@Override
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(),
helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
...
}
现在肯定能找到图片的缓存文件了,而当缓存文件不为空时,有这样一句:
modelLoaders = helper.getModelLoaders(cacheFile);
那么,根据我前文Glide之旅 —— Registry中获取方法可知,会返回一个包含以下对象的集合:
1. com.bumptech.glide.load.model.ByteBufferFileLoader.Factory#build(MultiModelLoaderFactory)
2. com.bumptech.glide.load.model.FileLoader.StreamFactory#build(MultiModelLoaderFactory)
3. com.bumptech.glide.load.model.FileLoader.FileDescriptorFactory#build(MultiModelLoaderFactory)
4. com.bumptech.glide.load.model.UnitModelLoader.Factory#build(MultiModelLoaderFactory)
依次执行集合中对象的buildLoadData方法,直到满足条件为止。显然,正常情况下,执行第一个ByteBufferFileLoader就能满足条件了,那么这个时候看下执行的源码:
package com.bumptech.glide.load.model;
...
public class ByteBufferFileLoader implements ModelLoader<File, ByteBuffer> {
...
@Override
public LoadData buildLoadData(File file, int width, int height, Options options) {
return new LoadData<>(new ObjectKey(file), new ByteBufferFetcher(file));
}
...
private static class ByteBufferFetcher implements DataFetcher<ByteBuffer> {
...
}
...
}
再来看看LoadData的源码
package com.bumptech.glide.load.model;
...
public interface ModelLoader<Model, Data> {
class LoadData {
public final Key sourceKey;
public final List alternateKeys;
public final DataFetcher fetcher;
public LoadData(Key sourceKey, DataFetcher fetcher) {
this(sourceKey, Collections.emptyList(), fetcher);
}
public LoadData(Key sourceKey, List alternateKeys, DataFetcher fetcher) {
this.sourceKey = Preconditions.checkNotNull(sourceKey);
this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
this.fetcher = Preconditions.checkNotNull(fetcher);
}
}
...
}
那么,下面这个执行流程就疏通了
loadData.fetcher.loadData(helper.getPriority(), this);
直接执行到ByteBufferFetcher#loadData(Priority, DataCallback super ByteBuffer>)
package com.bumptech.glide.load.model;
...
public class ByteBufferFileLoader implements ModelLoader<File, ByteBuffer> {
...
private static class ByteBufferFetcher implements DataFetcher<ByteBuffer> {
...
@Override
public void loadData(Priority priority, DataCallback super ByteBuffer> callback) {
ByteBuffer result = null;
try {
result = ByteBufferUtil.fromFile(file);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to obtain ByteBuffer for file", e);
}
callback.onLoadFailed(e);
return;
}
callback.onDataReady(result);
}
...
}
...
}
执行顺利的话,直接回调DataCacheGenerator#onDataReady(Object),注意到,这里的传参是一个ByteBuffer类型的参数
package com.bumptech.glide.load.engine;
...
class DataCacheGenerator implements DataFetcherGenerator, DataFetcher.DataCallback {
...
DataCacheGenerator(List cacheKeys, DecodeHelper> helper, FetcherReadyCallback cb) {
this.cacheKeys = cacheKeys;
this.helper = helper;
this.cb = cb;
}
...
@Override
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
@Override
public void onLoadFailed(Exception e) {
cb.onDataFetcherFailed(sourceKey, e, loadData.fetcher, DataSource.DATA_DISK_CACHE);
}
}
至此,DataFetcherGenerator的流程就梳理完了,执行顺利的话,接下来是开始回调DecodeJob#onDataFetcherReady(Key, Object, DataFetcher>, DataSource, Key)
package com.bumptech.glide.load.engine;
...
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
Runnable, Comparable>, Poolable {
...
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher> fetcher,
DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
decodeFromRetrievedData();
}
}
...
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
Resource resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
exceptions.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private void notifyEncodeAndRelease(Resource resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource result = resource;
LockedResource lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
onEncodeComplete();
}
}
private Resource decodeFromData(DataFetcher> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
Resource result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
private Resource decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath path = decodeHelper.getLoadPath((Class) data.getClass());
return runLoadPath(data, dataSource, path);
}
private Resource runLoadPath(Data data, DataSource dataSource,
LoadPath path) throws GlideException {
DataRewinder rewinder = glideContext.getRegistry().getRewinder(data);
try {
return path.load(rewinder, options, width, height,
new DecodeCallback(dataSource));
} finally {
rewinder.cleanup();
}
}
...
}
当执行到decodeFromFetcher(Data, DataSource)的内部时,需要根据ByteBuffer类型返回一个LoadPath的对象作为传参,关于这个传参的获取,在我的前文Glide之旅 —— Registry中进行了详细地梳理,而rewinder则是一个ByteBufferRewinder的实例,接下来执行com.bumptech.glide.load.engine.LoadPath#load(DataRewinder, Options, int, int, DecodePath.DecodeCallback
package com.bumptech.glide.load.engine;
...
public class LoadPath<Data, ResourceType, Transcode> {
...
public Resource load(DataRewinder rewinder, Options options, int width,
int height, DecodePath.DecodeCallback decodeCallback) throws GlideException {
List exceptions = listPool.acquire();
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, exceptions);
} finally {
listPool.release(exceptions);
}
}
private Resource loadWithExceptionList(DataRewinder rewinder, Options options,
int width, int height, DecodePath.DecodeCallback decodeCallback,
List exceptions) throws GlideException {
int size = decodePaths.size();
Resource result = null;
for (int i = 0; i < size; i++) {
DecodePath 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;
}
...
}
可以看到,主要还是要执行com.bumptech.glide.load.engine.DecodePath#decode(DataRewinder
package com.bumptech.glide.load.engine;
...
public class DecodePath<DataType, ResourceType, Transcode> {
...
public Resource decode(DataRewinder rewinder, int width, int height,
Options options, DecodeCallback callback) throws GlideException {
Resource decoded = decodeResource(rewinder, width, height, options);
Resource transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed);
}
private Resource decodeResource(DataRewinder rewinder, int width,
int height, Options options) throws GlideException {
List exceptions = listPool.acquire();
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
private Resource decodeResourceWithList(DataRewinder rewinder, int width,
int height, Options options, List exceptions) throws GlideException {
Resource result = null;
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options);
}
} catch (IOException 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;
}
...
}
在decodeResourceWithList方法中decoders就是我前文中得出来的
1. new DecodePath(ByteBuffer, GifDrawable, Drawable, [ByteBufferGifDecoder], UnitTranscoder, exceptionListPool)
2. new DecodePath(ByteBuffer, Bitmap, BitmapDrawable, [ByteBufferBitmapDecoder], BitmapDrawableTranscoder, exceptionListPool)
3. new DecodePath(ByteBuffer, BitmapDrawable, Drawable, [BitmapDrawableDecoder], UnitTranscoder, exceptionListPool)
其中一个DecodePath实例的第四个用中括号括起来的参数,表示一个集合,那么也就是说,直到在上面三个DecodePath实例的decoders中找到可以执行的对象
package com.bumptech.glide.load.resource.gif;
...
public class ByteBufferGifDecoder implements ResourceDecoder<ByteBuffer, GifDrawable> {
...
@Override
public boolean handles(ByteBuffer source, Options options) throws IOException {
return !options.get(DISABLE_ANIMATION)
&& ImageHeaderParserUtils.getType(parsers, source) == ImageHeaderParser.ImageType.GIF;
}
...
}
只要远程图片不是GIF类型的,那么这个对象肯定就不能继续执行
package com.bumptech.glide.load.resource.bitmap;
...
public class ByteBufferBitmapDecoder implements ResourceDecoder<ByteBuffer, Bitmap> {
private final Downsampler downsampler;
public ByteBufferBitmapDecoder(Downsampler downsampler) {
this.downsampler = downsampler;
}
@Override
public boolean handles(ByteBuffer source, Options options) throws IOException {
return downsampler.handles(source);
}
@Override
public Resource decode(ByteBuffer source, int width, int height, Options options)
throws IOException {
InputStream is = ByteBufferUtil.toStream(source);
return downsampler.decode(is, width, height, options);
}
}
再附上Dwonsaampler的源码
package com.bumptech.glide.load.resource.bitmap;
...
public final class Downsampler {
...
public boolean handles(ByteBuffer byteBuffer) {
// We expect downsampler to handle any available type Android supports.
return true;
}
public Resource decode(InputStream is, int outWidth, int outHeight,
Options options) throws IOException {
return decode(is, outWidth, outHeight, options, EMPTY_CALLBACKS);
}
public Resource decode(InputStream is, int requestedWidth, int requestedHeight,
Options options, DecodeCallbacks callbacks) throws IOException {
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(DOWNSAMPLE_STRATEGY);
boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
try {
Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
downsampleStrategy, decodeFormat, requestedWidth, requestedHeight,
fixBitmapToRequestedDimensions, callbacks);
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions, byte[].class);
}
}
...
}
显然,这里就返回了我们特定规格的图片了!而返回类型为Resource
package com.bumptech.glide.load.engine;
...
public class DecodePath<DataType, ResourceType, Transcode> {
...
public Resource decode(DataRewinder rewinder, int width, int height,
Options options, DecodeCallback callback) throws GlideException {
Resource decoded = decodeResource(rewinder, width, height, options);
Resource transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed);
}
...
}
回调com.bumptech.glide.load.engine.DecodeJob.DecodeCallback#onResourceDecoded(Resource
package com.bumptech.glide.load.engine;
...
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
Runnable, Comparable>, Poolable {
...
private final class DecodeCallback<Z> implements DecodePath.DecodeCallback<Z> {
private final DataSource dataSource;
@Synthetic
DecodeCallback(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Resource onResourceDecoded(Resource decoded) {
Class resourceSubClass = getResourceClass(decoded);
Transformation appliedTransformation = null;
Resource transformed = decoded;
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(decoded, width, height);
}
// TODO: Make this the responsibility of the Transformation.
if (!decoded.equals(transformed)) {
decoded.recycle();
}
final EncodeStrategy encodeStrategy;
final ResourceEncoder encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
encoder = decodeHelper.getResultEncoder(transformed);
encodeStrategy = encoder.getEncodeStrategy(options);
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
Resource result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
encodeStrategy)) {
if (encoder == null) {
throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
}
final Key key;
if (encodeStrategy == EncodeStrategy.SOURCE) {
key = new DataCacheKey(currentSourceKey, signature);
} else if (encodeStrategy == EncodeStrategy.TRANSFORMED) {
key = new ResourceCacheKey(currentSourceKey, signature, width, height,
appliedTransformation, resourceSubClass, options);
} else {
throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy);
}
LockedResource lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
return result;
}
@SuppressWarnings("unchecked")
private Class getResourceClass(Resource resource) {
return (Class) resource.get().getClass();
}
}
...
}
从上面的梳理结果可知,上面的泛型Z为Bitmap类型。
see you again