前言
DecodeJob的主要工作是从磁盘或者数据源(比如网络)中获取资源,并进行转换和转码。
class DecodeJob implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable>,
Poolable {
DecodeJob(DiskCacheProvider diskCacheProvider, Pools.Pool> pool) {
this.diskCacheProvider = diskCacheProvider;
this.pool = pool;
}
DecodeJob init(
GlideContext glideContext,
Object model,
EngineKey loadKey,
Key signature,
int width,
int height,
Class> resourceClass,
Class transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map, Transformation>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
boolean onlyRetrieveFromCache,
Options options,
Callback callback,
int order) {
decodeHelper.init(
glideContext,
model,
signature,
width,
height,
diskCacheStrategy,
resourceClass,
transcodeClass,
priority,
options,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
diskCacheProvider);
this.glideContext = glideContext;
this.signature = signature;
this.priority = priority;
this.loadKey = loadKey;
this.width = width;
this.height = height;
this.diskCacheStrategy = diskCacheStrategy; //在BaseRequestOptions中默认为DiskCacheStrategy.AUTOMATIC
this.onlyRetrieveFromCache = onlyRetrieveFromCache;
this.options = options;
this.callback = callback;
this.order = order;
this.runReason = RunReason.INITIALIZE; //关注点
this.model = model;
return this;
}
}
DecodeJob 实现了 Runnable 接口,它运行在 EngineJob 里面的线程池里。运行时首先会检查是否已经取消了执行,如果没有则执行 runWrapped()。
在 runWrapped() 中首先会判断当前的 runReason ,由于在 init() 中被赋值为 INITIALIZE ,则获取到的 stage 为 Stage.RESOURCE_CACHE ( Stage 表示当前执行到的阶段)。接着根据当前的 Stage 获取到的 DataFetcherGenerator 为 ResourceCacheGenerator,它用于从磁盘缓存中获取经过转化后的资源。不熟悉这个类的可以看下 Glide源码解析之ResourceCacheGenerator
@Override
public void run() {
DataFetcher> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (CallbackException e) {
throw e;
} catch (Throwable t) {
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
throw t;
} finally {
if (localFetcher != null) {
localFetcher.cleanup();
}
}
}
private void runWrapped() {
runWrappedCount++;
switch (runReason) { //在 init()中赋值为 INITIALIZE
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);
}
}
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
return diskCacheStrategy.decodeCachedResource() //默认为true
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData() //默认为true
? 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);
}
}
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);
}
}
加载数据
获取到 DataFetcherGenerator 后就开始将获取数据的操作交给它的 startNext() 来执行,这里我们以 ResourceCacheGenerator 成功获取为例,则 isStarted 会被赋值为 true ,不用进入循环。最终获取的数据会通过 DecodeJob 实现的 FetcherReadyCallback 的 onDataFetcherReady() 回调。
如果 ResourceCacheGenerator 没有获取到数据,由上可知则依次会交给 DataCacheGenerator (原数据的磁盘缓存) 和 SourceGenerator (从数据源加载,比如网络,不熟悉的可以看下 Glide源码解析之SourceGenerator)
private void runGenerators() {
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 加载数据成功后则通过 onDataFetcherReady() 回调给 DecodeJob ,由于 ResourceCacheGenerator 并没有切换线程去获取资源,所以会执行到 decodeFromRetrievedData() 去进行解码。经过一系列的调用,最终会将解码的操作交给 LoadPath 去执行。
//ResourceCacheGenerator
@Override
public void onDataReady(Object data) {
cb.onDataFetcherReady(sourceKey, data/*ByteBuffer*/, loadData.fetcher/*ByteBufferFetcher*/, DataSource.RESOURCE_DISK_CACHE,
currentKey);
}
//DecodeJob
@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 {
try {
decodeFromRetrievedData(); // 执行点
} finally {
GlideTrace.endSection();
}
}
}
private void decodeFromRetrievedData() {
Resource resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource); // 执行点
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private Resource decodeFromData(DataFetcher> fetcher, Data data,
DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
Resource result = decodeFromFetcher(data, dataSource); // 执行点
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 {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder rewinder = glideContext.getRegistry().getRewinder(data); // ByteBufferRewinder
try {
return path.load(
rewinder, options, width, height, new DecodeCallback(dataSource)); // 执行点
} finally {
rewinder.cleanup();
}
}
开始解码
会遍历 decodePaths 来寻找哪个 DecodePath 是能完成解码工作的,而具体解码由分为三步,第一步为解码资源,第二步为解码之后的工作(实际为对资源进行缓存),第三步为转码,即是将获取到的资源类型转为另一种资源类型。
//LoadPath
public Resource load(DataRewinder rewinder, @NonNull Options options, int width,
int height, DecodePath.DecodeCallback decodeCallback) throws GlideException {
List throwables = Preconditions.checkNotNull(listPool.acquire());
try {
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables); // 执行点
} finally {
listPool.release(throwables);
}
}
private Resource loadWithExceptionList(DataRewinder rewinder,
@NonNull Options options,
int width, int height, DecodePath.DecodeCallback decodeCallback,
List exceptions) throws GlideException {
Resource result = null;
for (int i = 0, size = decodePaths.size(); 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;
}
//DecodePath
public Resource decode(DataRewinder rewinder, int width, int height,
@NonNull Options options, DecodeCallback callback) throws GlideException {
Resource decoded = decodeResource(rewinder, width, height, options);
Resource transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
解码资源
当资源类型是 ByteBuffer 的时候,对应的 DataRewinder 是 ByteBufferRewinder ,ResourceDecoder 是 ByteBufferBitmapDecoder ,则最终的解码操作将由 ByteBufferBitmapDecoder 来完成。
@NonNull
private Resource decodeResource(DataRewinder rewinder, int width,
int height, @NonNull Options options) throws GlideException {
List exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions); //执行点
} finally {
listPool.release(exceptions);
}
}
@NonNull
private Resource decodeResourceWithList(DataRewinder rewinder /*ByteBufferRewinder*/, int width,
int height, @NonNull 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)) {
//ByteBufferBitmapDecoder
data = rewinder.rewindAndGet();
result = decoder.decode(data, width, height, options); //执行点
}
} catch (IOException | RuntimeException | OutOfMemoryError e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
首先会将 ByteBuffer 转化为 InputStream ,然后再交给 Downsampler来解码。
public class ByteBufferBitmapDecoder implements ResourceDecoder {
private final Downsampler downsampler;
public ByteBufferBitmapDecoder(Downsampler downsampler) {
this.downsampler = downsampler;
}
@Override
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) {
return downsampler.handles(source);
}
@Override
public Resource decode(@NonNull ByteBuffer source, int width, int height,
@NonNull Options options)
throws IOException {
InputStream is = ByteBufferUtil.toStream(source);
return downsampler.decode(is, width, height, options);
}
}
//Downsampler
public boolean handles(@SuppressWarnings("unused") ByteBuffer byteBuffer) {
return true;
}
//ByteBufferUtil
public static InputStream toStream(@NonNull ByteBuffer buffer) {
return new ByteBufferStream(buffer);
}
在解码操作里首先会根据 Bitmap 的宽高和 ImageView 的宽高来计算出缩放值,然后从 BitmapPool 获取一个合适宽高的 Bitmap 交给 BitmapFactory 去进行最终的解码操作,最后再判断是否需要对 Bitmap 进行旋转。
获取到 Bitmap 后将它包装进 BitmapResource 里返回,到此解码资源的工作就完成了。
//Downsampler
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 {
//省略取值代码
try {
Bitmap result = decodeFromWrappedStreams(is, bitmapFactoryOptions,
downsampleStrategy, decodeFormat, isHardwareConfigAllowed, requestedWidth,
requestedHeight, fixBitmapToRequestedDimensions, callbacks);
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions);
}
}
private Bitmap decodeFromWrappedStreams(InputStream is,
BitmapFactory.Options options, DownsampleStrategy downsampleStrategy,
DecodeFormat decodeFormat, boolean isHardwareConfigAllowed, int requestedWidth,
int requestedHeight, boolean fixBitmapToRequestedDimensions,
DecodeCallbacks callbacks) throws IOException {
//省略代码
//计算图片的缩放值,设置给 options
calculateScaling(
imageType,
is,
callbacks,
bitmapPool,
downsampleStrategy,
degreesToRotate,
sourceWidth,
sourceHeight,
targetWidth,
targetHeight,
options);
//计算配置
calculateConfig(
is,
decodeFormat,
isHardwareConfigAllowed,
isExifOrientationRequired,
options,
targetWidth,
targetHeight);
boolean isKitKatOrGreater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
if ((options.inSampleSize == 1 || isKitKatOrGreater) && shouldUsePool(imageType)) {
//不用缩放或者系统版本大于19,而且可以使用 BitmapPool
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 (expectedWidth > 0 && expectedHeight > 0) {
setInBitmap(options, bitmapPool, expectedWidth, expectedHeight); //去 BitmapPool 中获取期望宽高的 Bitmap (设置给 options.inBitmap)
}
}
Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool); //执行点
callbacks.onDecodeComplete(bitmapPool, downsampled); //空实现
Bitmap rotated = null;
if (downsampled != null) {
// 在上面 calculateScaling() 会修改 inDensity,这里设置回来
downsampled.setDensity(displayMetrics.densityDpi);
rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation); //旋转图片
if (!downsampled.equals(rotated)) {
//如果旋转了,则缓存旋转后的 Bitmap 。如果没旋转则还是原来的 Bitmap
bitmapPool.put(downsampled);
}
}
return rotated;
}
private static Bitmap decodeStream(InputStream is, BitmapFactory.Options options,
DecodeCallbacks callbacks, BitmapPool bitmapPool) throws IOException {
if (options.inJustDecodeBounds) {
is.mark(MARK_POSITION);
} else {
callbacks.onObtainBounds();
}
int sourceWidth = options.outWidth;
int sourceHeight = options.outHeight;
String outMimeType = options.outMimeType;
final Bitmap result;
TransformationUtils.getBitmapDrawableLock().lock();
try {
result = BitmapFactory.decodeStream(is, null, options); //最终还是靠 BitmapFactory 来生成 Bitmap 的
} catch (IllegalArgumentException e) {
IOException bitmapAssertionException =
newIoExceptionForInBitmapAssertion(e, sourceWidth, sourceHeight, outMimeType, options);
throw bitmapAssertionException;
} finally {
TransformationUtils.getBitmapDrawableLock().unlock();
}
return result;
}
编码资源
解码资源完成后接着就轮到编码资源了,由 DecodeCallback (接口) 去执行,在 DecodeJob 的 runLoadPath() 中给callback 赋的值是 DecodeCallback (实现类),而它将具体的编码操作又交回给了 DecodeJob 的 onResourceDecoded()去执行。
首先会判断资源是否可以编码,由上可知资源类型为 Bitmap ,是可以编码的。接着判断是否可以进行缓存,由于这里是从 ResourceCacheGenerator 获取数据的,本来就是从缓存中取得数据,所以不再需要进行缓存,最终会将源数据返回。
如果是需要缓存的则会交给 DeferredEncodeManager 进行磁盘缓存,如果对磁盘缓存不熟悉的可以看下 Glide源码解析之DiskCache
//DecodePath
public Resource decode(DataRewinder rewinder, int width, int height,
@NonNull Options options, DecodeCallback callback) throws GlideException {
Resource decoded = decodeResource(rewinder, width, height, options);
Resource transformed = callback.onResourceDecoded(decoded); //执行点
return transcoder.transcode(transformed, options);
}
private final class DecodeCallback implements DecodePath.DecodeCallback {
private final DataSource dataSource;
@Synthetic
DecodeCallback(DataSource dataSource) {
this.dataSource = dataSource;
}
@NonNull
@Override
public Resource onResourceDecoded(@NonNull Resource decoded) {
return DecodeJob.this.onResourceDecoded(dataSource, decoded);
}
}
Resource onResourceDecoded(DataSource dataSource,
@NonNull Resource decoded) {
Class resourceSubClass = (Class) decoded.get().getClass();
Transformation appliedTransformation = null;
Resource transformed = decoded;
// ResourceCacheGenerator 回调给 DecodeJob 的就是 DataSource.RESOURCE_DISK_CACHE
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height);
}
if (!decoded.equals(transformed)) {
decoded.recycle();
}
final EncodeStrategy encodeStrategy;
final ResourceEncoder encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) {
encoder = decodeHelper.getResultEncoder(transformed); // BitmapEncoder
encodeStrategy = encoder.getEncodeStrategy(options); // EncodeStrategy.TRANSFORMED
} else {
encoder = null;
encodeStrategy = EncodeStrategy.NONE;
}
Resource result = transformed;
//从 ResourceCacheGenerator 获取数据用的是 sourceKey ,所以这里是 false。
//因为我们本来就是从缓存中取的数据,所以并不需要再次缓存。
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
encodeStrategy)) {
//这里面是生成缓存的 Key ,并将缓存的操作交给 deferredEncodeManager 去执行
if (encoder == null) {
throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass());
}
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 lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}
return result;
}
//DeferredEncodeManager
void encode(DiskCacheProvider diskCacheProvider, Options options) {
try {
//进行磁盘缓存
diskCacheProvider.getDiskCache().put(key,
new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
}
}
转码
将 Resource
//BitmapDrawableTranscoder
public Resource transcode(@NonNull Resource toTranscode,
@NonNull Options options) {
return LazyBitmapDrawableResource.obtain(resources, toTranscode);
}
//LazyBitmapDrawableResource
public static Resource obtain(
@NonNull Resources resources, @Nullable Resource bitmapResource) {
if (bitmapResource == null) {
return null;
}
return new LazyBitmapDrawableResource(resources, bitmapResource);
}
public BitmapDrawable get() {
return new BitmapDrawable(resources /*这是 Android 里面的 Resource */, bitmapResource.get());
}
处理数据
让我们回到 DecodeJob 一开始获取数据的时候,在获取到数据之后通过回调将数据传给 EngineJob 。接着如果需要缓存的则调用 deferredEncodeManager 去执行,最后释放资源。到此 DecodeJob 的使命就全部完成了。
//DecodeJob
private void decodeFromRetrievedData() {
Resource resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.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 是用来调用磁盘缓存的,就是在这里调用
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
//释放资源
onEncodeComplete();
}
private void notifyComplete(Resource resource, DataSource dataSource) {
callback.onResourceReady(resource, dataSource);
}
//EngineJob
public void onResourceReady(Resource resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}