Glide 源码分析

前言

以前对 Glide 的认知一直停留在一行代码就可以完成图片加载,现在就来尝试探索下这一行代码下,内部到底做了些什么。本文基于Glide 4.8.0。

查阅官方文档发现 Glide 主要有以下优点:

  1. 调用方便,一行代码
  2. 无代码侵入,直接使用 ImageView 即可
  3. 扩展性强,快速集成 OkHttp 等等
  4. 支持 Gif 图片加载
  5. 生命周期及网络连接变化时自动管理请求

基本使用

使用 Glide 加载网络图片可以简单使用以下一行代码搞定。

Glide.with(this).load(URL).into(imageView)

如果需要指定默认图、错误图等,那么可以新建一个 RequestOptions 进行设置。

val options = RequestOptions().apply {
placeholder(R.drawable.ic_placeholder)
error(R.drawable.ic_error)
}
Glide.with(this).load(URL).apply(options).into(imageView)

还有很多其它用法就不展开了。

基本概念

在分析源码前,需要先了解以下几个类,以便于后续进行分析。

Target

Glide 通知外界加载过程的接口,主要有四个回调方法 onLoadStarted、onResourceReady、onLoadCleared、onLoadFailed 。

Encoder

编码器,用于将数据写入到某种持久化数据存储中。

Decoder

解码器,用于从某种持久化数据存储中读取数据。

Transcoder

转码器,用于将一种资源类型转换为另一种资源类型,比如 Bitmap 转换为 BitmapDrawable 等等。

Transformation

资源转换,不同于转码器,这个属于同种资源转换,比如进行裁减等等。

DataRewinder

倒带器(类似磁带回放),用于将数据的读取位置进行重置。比如读取了图片头后,又需要将其编码存储到文件中,这时就需要重置。

ModelLoaderFactory

数据模型加载器工厂,用于构建具体的数据模型加载器,基本都以对应 ModelLoader 类的内部类存在。

ModelLoader

数据模型加载器,用于将任意复杂的数据模型转换为具体的数据类型,比如将 String 转换为 InputSteam 等。

public interface ModelLoader<Model, Data> {
// 用于构建 LoadData 实例,如果无法构建那么返回 null
LoadData<Data> buildLoadData(@NonNull Model model, int width, int height,
@NonNull Options options);
// 如果当前的 ModelLoader 可以处理该数据源那么返回 true,否则返回 false
boolean handles(@NonNull Model model);
}

LoadData

加载资源的 Key 和用于加载该资源的 DataFetcher 包装对象。

class LoadData<Data> {
public final Key sourceKey;
public final List<Key> alternateKeys;
public final DataFetcher<Data> fetcher;
public LoadData(Key sourceKey, DataFetcher<Data> fetcher) {
this(sourceKey, Collections.<Key>emptyList(), fetcher);
}
public LoadData(Key sourceKey, List<Key> alternateKeys, DataFetcher<Data> fetcher) {
this.sourceKey = Preconditions.checkNotNull(sourceKey);
this.alternateKeys = Preconditions.checkNotNull(alternateKeys);
this.fetcher = Preconditions.checkNotNull(fetcher);
}
}

DecodePath

一条解码路径,dataClass => resourcesClass => transcodeClass,如 ByteBuffer => GifDrawable => Drawable。

LoadPath

多条解码路径(内部保证 dataClass、resourcesClass、transcodeClass 一致),内部包含若干个 DecodePath。

RequestManager

请求管理器,控制当前 Activity 或者当前应用所有请求的加载与暂停(会根据生命周期或者网络连接变化进行控制),每个 Activity 都可以拥有一个实例,应用级还可以拥有一个实例。

RequestManagerRetriever

请求管理器寻回者,用于新建一个 RequestManager 实例或者从 Activity 和 Fragment 中寻回已经存在的实例。

RequestBuilder

一个泛型类,可以处理泛型资源类型的选项设置和开始加载,也就是构建 Request。

Request

请求其用于加载资源到 Target ,一共就三个子类:

  1. SingleRequest 对应普通的请求

  2. ThumbnailRequestCoordinator 对应带缩略图的请求

  3. ErrorRequestCoordinator 对应带错误请求的请求

Engine

负责开始加载以及管理缓存(两级内存缓存以及磁盘缓存)

源码分析

Glide.with

Glide.with 提供了好多个重载方法,这里以参数为 FragmentActivity 的方法为例。

Glide.with

public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}

Glide.getRetriever

该方法用于获取到一个 RequestManagerRetriever 实例。

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
return get(context).getRequestManagerRetriever();
}
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
private static void checkAndInitializeGlide(@NonNull Context context) {
initializeGlide(context, new GlideBuilder());
}

由于最初 Glide 实例还未创建,因此会执行 checkAndInitializeGlide 方法创建 Glide 实例并进行初始化。

private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
// 获取注册的所有 GlideModule(APT 生成的或者清单文件中的),调用它们的 applyOptions 方法。
...
Glide glide = builder.build(applicationContext);
// 获取注册的所有 GlideModule(APT 生成的或者清单文件中的),调用它们的 registerComponent 方法。
...
Glide.glide = glide;
}

这里会涉及到自定义 GlideModule,这个可以根据官方文档编写,这里不关注,先跟主线,该方法核心就是 GlideBuilder.build 创建 Glide 实例。

Glide build(@NonNull Context context) {
if (sourceExecutor == null) sourceExecutor = GlideExecutor.newSourceExecutor();
if (diskCacheExecutor == null) diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
if (animationExecutor == null) animationExecutor = GlideExecutor.newAnimationExecutor();
if (memorySizeCalculator == null) memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
if (connectivityMonitorFactory == null) connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) bitmapPool = new LruBitmapPool(size);
else bitmapPool = new BitmapPoolAdapter();
}
if (arrayPool == null) arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
if (memoryCache == null) memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
if (diskCacheFactory == null) diskCacheFactory = new InternalCacheDiskCacheFactory(context);
if (engine == null) engine = new Engine(...);
RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory);
return new Glide(...);
}

如果自定义 GlideModule 中没有单独设置那么 sourceExecutor、diskCacheExecutor 等都使用默认值。这里创建了 Engine、RequestManagerRetriever 实例,最终创建了 Glide 实例。

Glide(...) {
registry = new Registry(); // 创建注册表
// 注册 ImageHeaderParser、Encoder、ResourceEncoder、ResourceDecoder
// Transcoder、ModelLoaderFactory、DataRewinder
...
// 新建 ImageViewTargetFactory 用于创建 BitmapImageViewTarget 或者 DrawableImageViewTarget 实例
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
// 新建 GlideContext 用于后续获取 Glide 组件
glideContext = new GlideContext(...);
}

注册表类 Registry 内部还包含了很多个注册表,结构如下。

public class Registry {
private final ModelLoaderRegistry modelLoaderRegistry;
private final EncoderRegistry encoderRegistry;
private final ResourceDecoderRegistry decoderRegistry;
private final ResourceEncoderRegistry resourceEncoderRegistry;
private final DataRewinderRegistry dataRewinderRegistry;
private final TranscoderRegistry transcoderRegistry;
private final ImageHeaderParserRegistry imageHeaderParserRegistry;
}

内部七个注册表分别对应 Glide 实例创建时注册的七种类型。注册表输入如下:

ModelLoaderRegistry
ModelClass DataClass ModelLoaderFactory
Bitmap Bitmap UnitModelLoader.Factory
GifDecoder GifDecoder UnitModelLoader.Factory
File ByteBuffer ByteBufferFileLoader.Factory
File InputStream FileLoader.StreamFactory
File ParcelFileDescriptor FileLoader.FileDescriptorFactory
File File UnitModelLoader.Factory
int InputStream ResourceLoader.StreamFactory
int ParcelFileDescriptor ResourceLoader.FileDescriptorFactory
int Uri ResourceLoader.UriFactory
int AssetFileDescriptor ResourceLoader.AssetFileDescriptorFactory
Integer InputStream ResourceLoader.StreamFactory
Integer ParcelFileDescriptor ResourceLoader.FileDescriptorFactory
Integer Uri ResourceLoader.UriFactory
Integer AssetFileDescriptor ResourceLoader.AssetFileDescriptorFactory
String InputStream DataUrlLoader.StreamFactory
String InputStream StringLoader.StreamFactory
String ParcelFileDescriptor StringLoader.FileDescriptorFactory
String AssetFileDescriptor StringLoader.AssetFileDescriptorFactory
Uri InputStream DataUrlLoader.StreamFactory
Uri InputStream HttpUriLoader.Factory
Uri InputStream AssetUriLoader.StreamFactory
Uri InputStream MediaStoreImageThumbLoader.Factory
Uri InputStream MediaStoreVideoThumbLoader.Factory
Uri InputStream UriLoader.StreamFactory
Uri InputStream UrlUriLoader.StreamFactory
Uri ParcelFileDescriptor AssetUriLoader.FileDescriptorFactory
Uri ParcelFileDescriptor UriLoader.FileDescriptorFactory
Uri AssetFileDescriptor UriLoader.AssetFileDescriptorFactory
Uri File MediaStoreFileLoader.Factory
Uri Uri UnitModelLoader.Factory
URL InputStream UrlLoader.StreamFactory
GlideUrl InputStream HttpGlideUrlLoader.Factory
byte[] InputStream ByteArrayLoader.StreamFactory
byte[] ByteBuffer ByteArrayLoader.ByteBufferFactory
EncoderRegistry
DataClass Encoder
ByteBuffer ByteBufferEncoder
InputSteam SteamEncoder
ResourceEncoderRegistry
DateClass ResourceEncoder
Bitmap BitmapEncoder
InputSteam BitmapDrawableEncoder
GifDrawable GifDrawableEncoder
ResourceDecoderRegistry
Bucket DataClass ResourceClass ResourceDecoder
Bitmap ByteBuffer Bitmap ByteBufferBitmapDecoder
Bitmap InputSteam Bitmap StreamBitmapDecoder
Bitmap ParcelFileDescriptor Bitmap VideoDecoder
Bitmap AssetFileDescriptor Bitmap VideoDecoder
Bitmap ByteBuffer Bitmap ByteBufferBitmapDecoder
BitmapDrawable ByteBuffer BitmapDrawable BitmapDrawableDecoder
BitmapDrawable InputStream BitmapDrawable BitmapDrawableDecoder
BitmapDrawable ParcelFileDescriptor BitmapDrawable BitmapDrawableDecoder
Gif InputSteam GifDrawable StreamGifDecoder
Gif ByteBuffer GifDrawable ByteBufferGifDecoder
legacy_append Uri Drawable ResourceDrawableDecoder
legacy_append Uri Bitmap ResourceBitmapDecoder
legacy_append File File FileDecoder
legacy_append Drawable Drawable UnitDrawableDecoder
DataRewinderRegistry
DataClass DataRewinder DataRewinder.Factory
InputStream InputStreamRewinder InputStreamRewinder.Factory
ByteBuffer ByteBufferRewinder ByteBufferRewinder.Factory
TranscoderRegistry
ResourceClass TranscodeClass ResourceTranscoder
Bitmap BitmapDrawable BitmapDrawableTranscoder
Bitmap byte[] BitmapBytesTranscoder
Drawable byte[] DrawableBytesTranscoder
GifDrawable byte[] GifDrawableBytesTranscoder
ImageHeaderParserRegistry
ImageHeaderParser
DefaultImageHeaderParser
ExifInterfaceImageHeaderParser

至此 getRetriever 方法结束了,返回一个新建的 RequestManagerRetriever 实例。

RequestManagerRetriever.get

该方法用于获取到一个 RequestManager 实例。

public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(
activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}

如果当前在子线程执行,那么直接当做 Application 进行处理,否则调用 supportFragmentGet 尝试去获取一个 RequestManager。

private RequestManager supportFragmentGet(Context context, FragmentManager fm, 
Fragment parentHint, boolean isParentVisible) {
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager = factory.build(...);
current.setRequestManager(requestManager);
}
return requestManager;
}

这里会涉及到通过监听生命周期来达到开启暂停加载图片,具体细节如下。

private SupportRequestManagerFragment getSupportRequestManagerFragment(...) {
SupportRequestManagerFragment current = fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
// 需要缓存的原因是 commitAllowingStateLoss 不是立即执行的,而是会发送一个消息
// 到主线程,移除缓存的消息由于再其后发送,因此一旦添加 Fragment 添加完成缓存就会被移除
current = this.pendingSupportRequestManagerFragments.get(fm); // 从缓存取,防止创建多个
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
this.pendingSupportRequestManagerFragments.put(fm, current); // 放入缓存
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
this.handler.obtainMessage(2, fm).sendToTarget(); // 移除缓存
}
}
return current;
}

get 方法其实就是向当前 Activity 添加一个看不见的 SupportRequestManagerFragment,主要看下该 Fragment 内部实现。

public class SupportRequestManagerFragment extends Fragment {
public SupportRequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
public SupportRequestManagerFragment(ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
public void onStart() {
super.onStart();
lifecycle.onStart();
}
public void onStop() {
super.onStop();
lifecycle.onStop();
}
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
}
}
class ActivityFragmentLifecycle implements Lifecycle {
// 弱引用 Set
private final Set<LifecycleListener> lifecycleListeners =
Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
private boolean isStarted;
private boolean isDestroyed;
public void addListener(@NonNull LifecycleListener listener) {
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
public void removeListener(@NonNull LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}
void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
}

SupportRequestManagerFragment 重写了 Fragment 的生命周期方法,增加调用 ActivityFragmentLifecycle 对应方法,进而通知 LifecycleListener,不过问题来了,addListener、removeListener 在哪里调用了?经过查询只会在 RequestManager 中被调用。

class RequestManager {
RequestManager(...) {
connectivityMonitor =
factory.build(
context.getApplicationContext(),
new RequestManagerConnectivityListener(requestTracker));
if (Util.isOnBackgroundThread()) {
mainHandler.post(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
}
public void onDestroy() {
lifecycle.removeListener(this);
}
}

也就是每当 Fragment 生命周期变化时,都会通知与之绑定的 RequestManager 因此也就可以根据生命周期控制加载和暂停。

RequestManager.load

RequestManager.load 有以下好几个重载方法,这里以参数为 String 的方法为例。

public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}

asDrawable 内部会创建一个 RequestBuilder 实例返回。

public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}

loadGeneric 将数据源(这里是请求地址)进行存储,然后就返回自身。

RequestBuilder.into

into 方法有以下好几个重载方法:

Screen Shot 2021-05-25 at 6.12.39 PM

暂时只关注下参数为 ImageView 类型的方法。

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// 根据不同的 View.getScaleType 创建对应的 RequestOptions
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass), null, requestOptions);
}

接着调用 GlideContext.buildImageViewTarget 创建 Target 实例。

public <X> ViewTarget<ImageView, X> buildImageViewTarget(ImageView imageView, Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public <Z> ViewTarget<ImageView, Z> buildTarget(ImageView view, Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}

这里由于前面调用了 asDrawable 因此 transcodeClass 为 Drawable,最终会创建一个 DrawableImageViewTarget 返回。继续跟进 into 方法。

private <Y extends Target<TranscodeType>> Y into(
Y target, RequestListener<TranscodeType> targetListener, RequestOptions options) {
Request request = buildRequest(target, targetListener, options);
Request previous = target.getRequest();
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}

buildRequest 用于构建 Request 实例。

private Request buildRequest(...) {
return buildRequestRecursive(...);
}
private Request buildRequestRecursive(...) {
// TODO 设置错误请求,暂时忽略
Request mainRequest =
buildThumbnailRequestRecursive(...);
return mainRequest;
}
private Request buildThumbnailRequestRecursive(...) {
// TODO 设置缩略图请求,暂时忽略
return obtainRequest(...);
}
private Request obtainRequest(...) {
return SingleRequest.obtain(...);
}

可以看到如果没有设置错误请求或者缩略图,那么最终创建的就是 SingleRequest 实例,初始状态为 PENDING。

track 主要用于追踪 Target、Request 以及开启请求。

void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
pendingRequests.add(request);
}
}

如果当前 RequestManager 没有暂停,那么开始请求,否则添加了等待队列中去,等待后续开始。

public void begin() {
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable()); // 展示占位图片
}
}

如果长宽已经被指定,那么直接调用 onSizeReady 继续下个流程。如果没有被指定,那么首先获取大小,获取后也会再调用 onSizeReady 继续下个流程。

接着如果图片正在加载,或者正在等待获取大小(获取大小方法为如果 View 还没进行测量,就设置一个 onPreDrawListener 等待测量完成后计算,如果设置了 wrap_content 那么会按照屏幕大小加载)回调 onLoadStarted 展示占位图片。

public void onSizeReady(int width, int height) {
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier); // 决定最终需要的宽
this.height = maybeApplySizeMultiplier(height, sizeMultiplier); // 决定最终需要的高
loadStatus = engine.load(///);
}

当加载的尺寸确定了以后就设置当前 Request 状态为 Running ,然后使用 Engine 去加载该图片。注:上面也说过 Engine 主要负责开启加载以及管理活动的或缓存的资源。

public <R> LoadStatus load(...) {
// cb 回调为 SingleRequest 对象
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options); // 构建缓存 key,注意长宽也是组成成分
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); // 从正活跃的资源中获取
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE); // 回调加载成功
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); // 从内存缓存中获取
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE); // 回调加载成功
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) { // 找到相同的任务了,那么不再重新开始加载,只是添加一个回调
current.addCallback(cb);
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob = engineJobFactory.build(...);
DecodeJob<R> decodeJob = decodeJobFactory.build(...);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}

首先从正活跃的资源缓存中获取资源,如果获取不到再从内存缓存中获取资源,还是获取不到则创建 EngineJob 实例前去加载。由于前面两级内存缓存还没内容,因此先看 EngineJob.start 。

public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache() // 选择合适的执行器,默认支持磁盘缓存,因此选择 diskCacheExecutor,该执行器会在 Glide 初始化时创建
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}

最终在线程池中执行了 DecodeJob ,跟进其 run 方法。

public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
if (isCancelled) { // 如果已经被取消了就回调 EngineJob.onLoadFailed 通知其加载失败
notifyFailed();
return;
}
runWrapped();
} catch (Throwable t) {
// ignore it.
}
}

内部就简单处理了下取消的情况,然后又调用了 runWrapped。

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;
}
}
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;
}
}
private void runGenerators() {
boolean isStarted = false;
while (!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
}

runWrapped 为 DecodeJob 的控制中枢,它决定了每一步需要执行的操作。

RESOURCE_CACHE

初始时 runReason 为 INITIALZIE,获取到的 nextStage 为 RESOURCE_CACHE 表明从磁盘缓存中读取转化过的资源,对应ResourceCacheGenerator 类。根据 runGenerators 的逻辑,跟进其 startNext 方法。

public boolean startNext() {
List<Key> sourceIds = helper.getCacheKeys();
List<Class<?>> resourceClasses = helper.getRegisteredResourceClasses();
while (modelLoaders == null || !hasNextModelLoader()) {
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false; // 首次由于没有缓存,这里返回 false
}
resourceClassIndex = 0;
}
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
currentKey = new ResourceCacheKey(...);
cacheFile = helper.getDiskCache().get(currentKey); // 从磁盘缓存中读取转化后的图片文件
if (cacheFile != null) {
sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> 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;
}

这个方法主要是从磁盘缓存中查询是否存在转换后的图片资源,如果存在再进行解码,这里先跳过,后续分析缓存的时候再看。首次该方法由于没有缓存,最终会返回 false。接着会进入下一步。

DecodeHelp.getCacheKeys

getCacheKeys 用于获取该数据源类型所有可能的缓存 Key。

List<Key> getCacheKeys() {
List<LoadData<?>> loadData = getLoadData();
for (int i = 0, size = loadData.size(); i < size; i++) {
LoadData<?> data = loadData.get(i);
if (!cacheKeys.contains(data.sourceKey)) {
cacheKeys.add(data.sourceKey);
}
for (int j = 0; j < data.alternateKeys.size(); j++) {
if (!cacheKeys.contains(data.alternateKeys.get(j))) {
cacheKeys.add(data.alternateKeys.get(j));
}
}
}
return cacheKeys;
}
List<LoadData<?>> getLoadData() {
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
LoadData<?> current =
modelLoader.buildLoadData(model, width, height, options);
if (current != null) {
loadData.add(current);
}
}
return loadData;
}

首先从注册表中获取所有支持该数据源转换的 ModelLoader ,然后分别让它们去构建 LoadData,最后获取所有构建完成的 LoadData.sourceKey 返回。由于一个 ModelLoader 可以会依赖于另一个 ModelLoader 因此获取所有支持该数据源转换的 ModelLoader 较为复杂。具体细节代码如下:

// Registry.java
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(@NonNull Model model) {
List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
if (result.isEmpty()) {
throw new NoModelLoaderAvailableException(model);
}
return result;
}

实际是从 ModelLoaderRegistry 中去获取所有支持该类型的 ModelLoader。

public <A> List<ModelLoader<A, ?>> getModelLoaders(@NonNull A model) {
List<ModelLoader<A, ?>> modelLoaders = getModelLoadersForClass(getClass(model));
int size = modelLoaders.size();
boolean isEmpty = true;
List<ModelLoader<A, ?>> filteredLoaders = Collections.emptyList();
for (int i = 0; i < size; i++) {
ModelLoader<A, ?> loader = modelLoaders.get(i);
if (loader.handles(model)) {
if (isEmpty) {
filteredLoaders = new ArrayList<>(size - i);
isEmpty = false;
}
filteredLoaders.add(loader);
}
}
return filteredLoaders;
}

这段代码意思很简单,首先获取所有支持该数据源类型的 ModelLoader 列表,然后过滤支持当前数据源的 ModelLoader,为什么需要过滤?原因是有些 ModelLoader 虽然说支持数据源类型为 String,但是它对这个字符串也有着要求,比如其只支持以 data:image 开头的字符串,那么其实核心还是在 getModelLoadersForClass 这个方法中。

private synchronized <A> List<ModelLoader<A, ?>> getModelLoadersForClass(Class<A> modelClass) {
List<ModelLoader<A, ?>> loaders = cache.get(modelClass);
if (loaders == null) {
loaders = Collections.unmodifiableList(multiModelLoaderFactory.build(modelClass));
cache.put(modelClass, loaders);
}
return loaders;
}

这里有一层缓存,不过初始时并不存在,因此会通过 MultiModelLoaderFactory 去获取支持该数据源类型的 ModelLoader。

注册关系为 Registry => ModelLoaderRegistry => MultiModelLoaderFactory,所有注册的 ModelLoaderFactory 其实都在 MultiModelLoaderFactory 里面。

synchronized <Model> List<ModelLoader<Model, ?>> build(@NonNull Class<Model> modelClass) {
try {
List<ModelLoader<Model, ?>> loaders = new ArrayList<>();
for (Entry<?, ?> entry : entries) {
// 避免出现死循环,最后导致栈溢出
if (alreadyUsedEntries.contains(entry)) {
continue;
}
if (entry.handles(modelClass)) {
alreadyUsedEntries.add(entry);
loaders.add(this.<Model, Object>build(entry)); // 1
alreadyUsedEntries.remove(entry);
}
}
return loaders;
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}

上述代码其实就是过滤所有支持该数据源的 ModelLoader,但是复杂就在于上述注释一处,某些 ModelLoaderFactory 在构建具体的 ModelLoader 实例时又会去调用 MultiModelLoaderFactory 的以下一个重载 build 方法,这里以 StringLoader.StreamFactory 举例说明。

public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
public ModelLoader<String, InputStream> build(MultiModelLoaderFactory multiFactory) {
return new StringLoader<>(multiFactory.build(Uri.class, InputStream.class));
}
}

上述代码又会去寻找所有支持将 Uri 转换为 InputSteam 的 ModelLoader,那么继续跟进另一个 build 方法。

public synchronized <Model, Data> ModelLoader<Model, Data> build(Class<Model> modelClass,
Class<Data> dataClass) {
try {
List<ModelLoader<Model, Data>> loaders = new ArrayList<>();
boolean ignoredAnyEntries = false;
for (Entry<?, ?> entry : entries) {
if (alreadyUsedEntries.contains(entry)) {
ignoredAnyEntries = true;
continue;
}
if (entry.handles(modelClass, dataClass)) {
alreadyUsedEntries.add(entry);
loaders.add(this.<Model, Data>build(entry));
alreadyUsedEntries.remove(entry);
}
}
if (loaders.size() > 1) {
return factory.build(loaders, throwableListPool);
} else if (loaders.size() == 1) {
return loaders.get(0);
} else {
if (ignoredAnyEntries) {
return emptyModelLoader();
} else {
throw new NoModelLoaderAvailableException(modelClass, dataClass);
}
}
} catch (Throwable t) {
alreadyUsedEntries.clear();
throw t;
}
}

大体上与一开始的 build 方法类似,不同点只在于最终如果查询到多个 ModelLoader 那么会封装成一个 MultiModelLoader 进行返回。这里可能会递归调用。举个例子,如果起始时数据源类型为 String,那么最终就会寻找到如下 ModelLoader 集。

String

=> DataUrlLoader(String => InputSteam)

=> StringLoader(String => InputSteam Recur to Uri => InputSteam

​ => DataUrlLoader(Uri => InputSteam)

​ => HttpUriLoader(Uri => InputSteam Recur to GlideUrl => InputSteam

​ => HttpGlideUrlLoader(GlideUrl => InputSteam)

​ => AssetUriLoader(Uri => InputSteam)

​ => MediaStoreImageThumbLoader(Uri => InputSteam)

​ => MediaStoreVideoThumbLoader(Uri => InputSteam)

​ => UriLoader(Uri => InputSteam)

​ => UrlUriLoader(Uri => InputSteam Recur to GlideUrl => InputSteam

​ => HttpGlideUrlLoader(GlideUrl => InputSteam)

=> StringLoader(String => ParcelFileDescriptor Recur to Uri => ParcelFileDescriptor

​ => AssetUriLoader(Uri => ParcelFileDescriptor)

​ => UriLoader(Uri => ParcelFileDescriptor)

=> StringLoader(String => AssetFileDescriptor Recur to Uri => AssetFileDescriptor

​ => UriLoader(Uri => AssetFileDescriptor)

过滤完所有 ModelLoader 以后就会分别调用其 buildLoadData 方法前去构建 LoadData。例子中最终只会返回一个 LoadData 其 key 为 url,fetcher 为 MultiFetcher 内部包含两个 HttpUrlFetcher,因为根据上文 StringLoader => HttpUriLoader => HttpGlideLoader 以及 StringLoader => UrlUriLoader => HttpGlideUrlLoader 都满足需求。

最终返回 [url]。

DecodeHelper.getRegisteredResourceClasses

getRegisteredResourceClasses 用于获取所有能转换为目标资源类型的类型

public <Model, TResource, Transcode> List<Class<?>> getRegisteredResourceClasses(
List<Class<?>> result = new ArrayList<>();
List<Class<?>> dataClasses = modelLoaderRegistry.getDataClasses(modelClass); // 1
for (Class<?> dataClass : dataClasses) {
List<? extends Class<?>> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass); // 2
for (Class<?> registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses = transcoderRegistry
.getTranscodeClasses(registeredResourceClass, transcodeClass); // 3
if (!registeredTranscodeClasses.isEmpty() && !result.contains(registeredResourceClass)) {
result.add(registeredResourceClass);
}
}
}
return result;
}

这个方法挺复杂的主要工作包括:

  1. 获取当前输入源能转换成的所有类型,对于 String 类型根据 ModelLoader 注册表,可以得出一共可以转换为 InputSteam、ParcelFileDescriptor、AccessFileDescriptor 三种。
  2. 对于每种可转换类型,分别获取解码后获得的资源类型,根据 ResourceDecoder 注册表,InputSteam 可以被解码成 GifDrawable、 Bitmap、BitmapDrawable;ParcelFileDescriptor 可以被解码成 Bitmap、BitmapDrawable;AccessFileDescriptor 可以被解码成 Bitmap。
  3. 对于每种解码后资源类型,查询其是否能被转码成目标类型,对于可以转换的(包括强转或者使用转码器)加入到 Result 中进行返回。

最终返回 GifDrawable、Bitmap 以及 BitmapDrawable。

DATA_CACHE

DATA_CACHE 表明从磁盘缓存中读取原始图片资源,对应 DataCacheGenerator 类。根据 runGenerators 的逻辑,跟进其 startNext 方法。

public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false; // 首次由于没有缓存,这里返回 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<File, ?> 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;
}

这个方法主要是从磁盘缓存中查询是否存在原始图片资源,如果存在再进行解码,这里先跳过,后续分析缓存的时候再看。首次进入该方法由于没有缓存,最终会返回 false。接着会进入下一步。

SOURCE

DATA_CACHE 表明从数据源处获取图片资源,对应 SourceGenerator 类。根据 runGenerators 的逻辑,跟进其 startNext 方法(这里会进行 reschedule 流程,不过还是会调用)。

public boolean startNext() {
... // 下面会分析,这里跳过
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}

终于到了加载资源的地方了,前面说过这里只有一个 LoadData ,该 LoadData 中会存在一个 MultiFetcher,最里面就是两个 HttpUrlFetcher(Why?),直接看 HttpUrlFetcher.loadData 方法看看其是如何加载资源的。

// HttpUrlFetcher.java
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
callback.onLoadFailed(e);
}
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) {
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
... // 设置 urlconnection
urlConnection.connect();
stream = urlConnection.getInputStream();
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
... // 重定向处理
} else {
throw new HttpException(statusCode);
}
}

最终就是通过 HttpUrlConnection 发起请求获取到 InputSteam(响应体),回调 SourceGenerator 的 onDataReady 方法。

public void onDataReadyInternal(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) { // 默认允许缓存
dataToCache = data;
cb.reschedule();
} else {
cb.onDataFetcherReady(...);
}
}

这里又调用了 reschedule ,最终又会重新调用一次 SourceGenerator.startNext 方法。

public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { // cacheData 方法调用完后就不为空了
return true;
}
... // 上面已经分析过
}
private void cacheData(Object dataToCache) {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer = new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
helper.getDiskCache().put(originalKey, writer);
sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}

本次进来是 dataToCache 已经不为空了(已经是刚刚请求回来的 InputSteam 了),调用 cacheData 将其缓存下来,而缓存流程为首先获取到编码器,通过查询注册表,编码器对应类为 SteamEncoder ,接着调用 DiskLruCacheWrapper.put 进行缓存。

public void put(Key key, Writer writer) {
String safeKey = safeKeyGenerator.getSafeKey(key);
DiskLruCache diskCache = getDiskCache();
Value current = diskCache.get(safeKey);
if (current != null) {
return;
}
DiskLruCache.Editor editor = diskCache.edit(safeKey);
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
}

内部使用了 DiskLruCache ,然后通过编码器,将刚刚的响应数据写入到缓存文件中,缓存完毕后又会调用 DataCacheGenerator.startNext 不过这回有缓存了。

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); // 由于有缓存,因此这里能获取到 cacheFile 了。
if (cacheFile != null) {
this.sourceKey = sourceId;
// 获取能处理输入源为 File 的 ModelLoader 并退出当前 while 循环往下执行。
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> 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;
}

由于现在原图已经有了缓存,因此会退出上面的 while 循环进入下面的循环,下面的循环会遍历所有能转换 File 的 ModelLoader,经过查询注册表,发现有以下这些。

![Screen Shot 2021-05-28 at 9.38.02 AM](/Users/hefuwei/Library/Application Support/typora-user-images/Screen Shot 2021-05-28 at 9.38.02 AM.png)

对每个 ModelLoader 调用 buildLoadData 方法构建 LoadData ,选择第一个不为空的并且存在一条完整加载路径的 loadData 调用loadData.fetcher.loadData 加载数据。那么具体该怎么理解完整加载路径呢?Glide 内部其实是通过 LoadPath 来表示加载路径的,通过分析 DecodeHelper.hasLoadPath 就可以了解。

// DecodeHelper.java
boolean hasLoadPath(Class<?> dataClass) {
return getLoadPath(dataClass) != null;
}
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
LoadPath<Data, TResource, Transcode> result =
loadPathCache.get(dataClass, resourceClass, transcodeClass);
if (loadPathCache.isEmptyLoadPath(result)) {
return null;
} else if (result == null) {
List<DecodePath<Data, TResource, Transcode>> decodePaths =
getDecodePaths(dataClass, resourceClass, transcodeClass);
if (decodePaths.isEmpty()) {
result = null;
} else {
result = new LoadPath<>(
dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}

这里主要是处理了一层缓存,忽略缓存,首先调用 getDecodePaths 去获取所有可以满足条件的解码路径,然后使用其构建 LoadPath。那么什么叫满足条件的解码路径呢?继续来分析 getDecodePaths 方法。

private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
List<Class<TResource>> registeredResourceClasses =
decoderRegistry.getResourceClasses(dataClass, resourceClass); // 1
for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses =
transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass); // 2
for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
List<ResourceDecoder<Data, TResource>> decoders =
decoderRegistry.getDecoders(dataClass, registeredResourceClass); // 3
ResourceTranscoder<TResource, Transcode> transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass); // 4
DecodePath<Data, TResource, Transcode> path =
new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass,
decoders, transcoder, throwableListPool);
decodePaths.add(path);
}
}
return decodePaths;
}

乍一看这段代码好像很难理解,不过举个例子就会很容易理解,首先选择了 ByteBufferFileLoader,因此 dataClass 为 ByteBuffer.class,resourceClass 为 Object.class(默认),transcodeClasses 为 Drawable.class(asDrawable 设置),一条完整的解码路径为,如何从 ByteBuffer 解码成 Object,然后如何从 Object 转码成 Drawable。下面分析代码

注释一获取到所有能将 ByteBuffer 解码成的类(GifDrawable、Bitmap、BitmapDrawable)。

注释二获取到有几种方式能将解码完成的资源(GifDrawable、Bitmap、BitmapDrawable 之一)转码成 Drawable。

注释三获取到支持转换的所有解码器(如支持 ByteBuffer 转换为 GifDrawable)。

注释四获取到支持转码的所有转码器(如支持 GifDrawable 转换为 Drawable)。注意:如果可以直接强转会返回 UnitTranscoder。

经过分析 LoadPath 的意义其实很明确了,其实就是 dataClass => resourceClass => transcodeClass 的所有解码路径集合。

回到上面,由于值选择第一个不为空的并且存在一条完整加载路径的 loadData 调用 loadData.fetcher.loadData 加载数据,因此 DataCacheGenerator.startNext 其实就可以理解成最终调用了 ByteBufferFileLoader.ByteBufferFetcher(resourceClass 为 Object,transcodeClass 为 Drawable),跟进代码。

public void loadData(@NonNull Priority priority, DataCallback<? super ByteBuffer> callback) {
ByteBuffer result;
try {
result = ByteBufferUtil.fromFile(file);
} catch (IOException e) {
callback.onLoadFailed(e);
return;
}
callback.onDataReady(result); // callback 指的是 DataCacheGenerator
}
// DataCacheGenerator.java
public void onDataReady(Object data) {
// cb 指的是 SourceGenerator,固定传入 dataSource 为 DATA_DISK_CACHE,但是对于 SourceGenerator 而言是错误的
cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey);
}
// SourceGenerator.java
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
// 官方注释说明了,传入的 dataSource 是错的,因此需要重新根据原始的 fetcher 重新进行设置,这里 cb 指的是 DecodeJob
cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
}
// DecodeJob.java
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();
}
}
}

内部也就是通过 NIO 将数据读取进了 ByteBuffer 中,然后一步步回调到 DecodeJob.onDataFetcherReady,接着不需要管线程判断,因此不管怎么样都会调用 decodeFromRetrievedData 。

// DecodeJob.java
private void decodeFromRetrievedData() {
Resource<R> resource = decodeFromData(currentFetcher, currentData, currentDataSource);;
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
private <Data> Resource<R> decodeFromData(DataFetcher<?> fetcher, Data data,
DataSource dataSource) throws GlideException {
return decodeFromFetcher(data, dataSource);;
}
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource) {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
LoadPath<Data, ResourceType, R> path){
Options options = getOptionsWithHardwareConfig(dataSource); // 决定是否支持硬件位图,Android 8 新增
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data); // 返回 ByteBufferRewinder
try {
return path.load(rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}

最终根据 LoadPath 解码并转码资源,获取到最终的资源。对于 ByteBuffer => Object => Drawable 一共有以下几条路径

![Screen Shot 2021-05-28 at 10.54.58 AM](/Users/hefuwei/Library/Application Support/typora-user-images/Screen Shot 2021-05-28 at 10.54.58 AM.png)

接下来就开始真正的进行加载了,跟进 LoadPath.load 方法。

// LoadPath.java
public Resource<Transcode> load(...) throws GlideException {
return loadWithExceptionList(...);
}
private Resource<Transcode> loadWithExceptionList(...) throws GlideException {
Resource<Transcode> result = null;
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions)); // 这里抛出异常会被 DecodeJob.decodeFromRetrievedData 收集
}
return result;
}

LoadPath 又委托给 DecodePath 进行加载,只要有一个成功那么就直接退出循环,并返回。

public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options); // 解码资源
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded); // 解码完成,通知 DecodeJob 应用 transform 及初始化缓存
return transcoder.transcode(transformed, options); // 转码资源
}

方法主要分为以下三步:

  1. 解码资源,如 ByteBuffer => GifDrawable
  2. 解码完成回调 DecodeJob 将解码后的资源缓存
  3. 转码资源,用于最终显示。
decodeResources

解码资源就是通过解码器进行解码

private Resource<ResourceType> decodeResource(...) throws GlideException {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
}
private Resource<ResourceType> decodeResourceWithList(...) {
Resource<ResourceType> result = null;
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> 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 | RuntimeException | OutOfMemoryError e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}

遍历所有 Decoder 调用 handle 方法判断是否能解码,如果可以调用 decode 方法真正解码,只要有一个解码器返回成功就退出循环。

对于 ByteBufferGifDecoder

// ByteBufferDecoder.java
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) throws IOException {
return !options.get(GifOptions.DISABLE_ANIMATION)
&& ImageHeaderParserUtils.getType(parsers, source) == ImageType.GIF;
}

只要不禁用 Gif ,那么最终会通过 HeaderParser 读取文件头,判断是否是 Gif 格式,具体细节如下。

private ImageType getType(Reader reader) throws IOException {
final int firstTwoBytes = reader.getUInt16();
if (firstTwoBytes == EXIF_MAGIC_NUMBER) { // JPEG
return JPEG;
}
final int firstFourBytes = (firstTwoBytes << 16 & 0xFFFF0000) | (reader.getUInt16() & 0xFFFF);
if (firstFourBytes == PNG_HEADER) { // PNG
reader.skip(25 - 4);
int alpha = reader.getByte();
return alpha >= 3 ? PNG_A : PNG;
}
if (firstFourBytes >> 8 == GIF_HEADER) { // GIF
return GIF;
}
// WEBP 判断逻辑忽略
return ImageType.WEBP;
}

可以看到通过读取两个字节就能判断是否是 JPEG,通过读取三个字节就能判断是否是 GIF,通过读取四个字节就能判断是否是 PNG。

如果前三个字节为 0x474946 那么就是 GIF 图片,Glide 也提供了选项将 Gif 当做普通图片处理(GifOptions.DISABLE_ANIMATION)

private GifDrawableResource decode(
ByteBuffer byteBuffer, int width, int height, GifHeaderParser parser, Options options) {
long startTime = LogTime.getLogTime();
try {
final GifHeader header = parser.parseHeader(); // 解析 Gif 头
Bitmap.Config config = options.get(GifOptions.DECODE_FORMAT) == DecodeFormat.PREFER_RGB_565
? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;
int sampleSize = getSampleSize(header, width, height); // 计算缩放比
GifDecoder gifDecoder = gifDecoderFactory.build(provider, header, byteBuffer, sampleSize);
gifDecoder.setDefaultBitmapConfig(config);
gifDecoder.advance();
Bitmap firstFrame = gifDecoder.getNextFrame();
Transformation<Bitmap> unitTransformation = UnitTransformation.get();
GifDrawable gifDrawable =
new GifDrawable(context, gifDecoder, unitTransformation, width, height, firstFrame);
return new GifDrawableResource(gifDrawable);
}
}

具体是如何解码这个 Gif 文件逻辑就不看了,因为这个需要了解 Gif 文件格式(后续有兴趣再看),最终返回了一个 GifDrawableResource 内部包含了 GifDrawable。

对于 ByteBufferBitmapDecoder

// ByteBufferBitmapDecoder.java
public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) {
return downsampler.handles(source); // 必定返回 true
}

Downsampler 的 handles 方法必定返回 true,继续看 decode 方法。

public Resource<Bitmap> decode(ByteBuffer source, int width, int height, Options options)
throws IOException {
InputStream is = ByteBufferUtil.toStream(source);
return downsampler.decode(is, width, height, options);
}
public Resource<Bitmap> decode(InputStream is, int requestedWidth, int requestedHeight,
Options options, DecodeCallbacks callbacks) throws IOException {
Bitmap result = decodeFromWrappedStreams(...);
return BitmapResource.obtain(result, bitmapPool);
}

decodeFromWrappedStreams 会通过 BitmapFactory 加载图片,并对图片进行缩放、翻转,代码过于复杂就不贴了。

onResourceDecoded

解码完成后,需要通过 DecodeJob ,回调其 onResourceDecoded 方法。

<Z> Resource<Z> onResourceDecoded(DataSource dataSource, Resource<Z> decoded) {
Class<Z> resourceSubClass = (Class<Z>) decoded.get().getClass();
Transformation<Z> appliedTransformation = null;
Resource<Z> transformed = decoded;
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
appliedTransformation = decodeHelper.getTransformation(resourceSubClass);
transformed = appliedTransformation.transform(glideContext, decoded, width, height); // 应用 Transform.
}
final EncodeStrategy encodeStrategy;
final ResourceEncoder<Z> encoder;
if (decodeHelper.isResourceEncoderAvailable(transformed)) { // 是否有对应的 ResourceEncoder 可以编码该资源
encoder = decodeHelper.getResultEncoder(transformed);
encodeStrategy = encoder.getEncodeStrategy(options);
}
Resource<Z> result = transformed;
boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey);
// 判断该转换后的资源是否要进行缓存(Encode 操作)
if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource, encodeStrategy)) {
final Key key;
switch (encodeStrategy) {
case SOURCE:
key = new DataCacheKey(currentSourceKey, signature);
break;
case TRANSFORMED:
key = new ResourceCacheKey(...);
break;
}
LockedResource<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult); // 延迟进行 Encode 操作。
result = lockedResult;
}
return result;
}

方法内部主要做了以下几个操作:

  1. 应用 Transform,可以通过 RequestOption 设置。
  2. 检查是否存在编码器可以编码该资源。
  3. 判断当前资源是否需要进行编码(缓存)。

默认的 DiskCacheStrategy 为 AUTOMATIC ,当加载远程数据(比如,从URL下载)时,该策略仅会存储原始数据,因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,该策略则会仅存储变换过的缩略图,因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。 远程数据存储原始图片,本地数据存储转换后的图片

transcode

应用完 transform 以后,又会对资源进行转码,其实根据转码注册表,也就是 Bitmap 能转化为 Drawable,其它都只能转化为 bytes[]。Bitmap 转换为 Drawable 采用方式为创建一个 BitmapDrawable。

至此资源解码、转换、转码成功,现在又要回到解码前,也就是 DecodeJob.decodeFromRetrievedData 。

private void decodeFromRetrievedData() {
Resource<R> resource = decodeFromData(currentFetcher, currentData, currentDataSource);
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource); // 通知进行缓存(编码)
} else {
runGenerators(); // 换下一条路,原先 File => ByteBuffer,后续改为 File => FileInputSteam
}
}

主要分析下 notifyEncodeAndRelease 方法。

private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
Resource<R> result = resource;
LockedResource<R> lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
notifyComplete(result, dataSource); // 通知 EngineJob 资源以及准备完成
stage = Stage.ENCODE;
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options); // 对资源进行编码写入磁盘缓存
}
onEncodeComplete();
}

方法内部首先会通知 EngineJob 资源已经准备完毕,然后如果需要缓存那么进行缓存,缓存完毕后清理下资源。重点看看 EngineJob 收到资源完成后做了什么。

// EngineJob.java
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
public boolean handleMessage(Message message) {
EngineJob<?> job = (EngineJob<?>) message.obj;
switch (message.what) {
case MSG_COMPLETE:
job.handleResultOnMainThread();
break;
}
return true;
}
void handleResultOnMainThread() {
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
engineResource.acquire(); // 引用计数加一(防止还没分发完就被回收)
listener.onEngineJobComplete(this, key, engineResource); // 缓存到 activeResources 中(弱引用缓存中)
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire(); // 每分发一次引用计数加一
cb.onResourceReady(engineResource, dataSource); // 通知 SingleRequest 资源准备完成,最终通知 Target 资源准备完成
}
}
engineResource.release(); // 引用计数减一(抵消循环前的)
release(false /*isRemovedFromQueue*/);
}

至此图片已经加载完毕,并交付给了 Target。

注:关于硬件位图

注:Glide V3 默认使用 RGB_565 ,Glide V4 默认使用 ARGB_8888。

缓存机制

Glide 分为内存以及磁盘两级缓存,其中内存又分为弱引用缓存以及一个 LRU 缓存,磁盘缓存又分为转换后的资源缓存以及原图缓存,缓存由 DiskCacheStrategy 控制,除原图缓存其它的缓存键都与加载的图片长宽有关。

public abstract class DiskCacheStrategy {
// 是否缓存转化后的数据
public abstract boolean isResourceCacheable(boolean isFromAlternateCacheKey,
DataSource dataSource, EncodeStrategy encodeStrategy);
// 是否缓存源数据,未转化过的
public abstract boolean isDataCacheable(DataSource dataSource);
// 是否解码缓存转化后的数据
public abstract boolean decodeCachedResource();
// 是否解码缓存的源数据,未转化过的
public abstract boolean decodeCachedData();
}

缓存主要分为以下情况(以加载网络图片为例):

  1. 无缓存的网络图片加载流程。

缓存一

  1. 有弱应用缓存的网络图片加载流程

缓存二

  1. 无弱引用缓存有 Lru 内存缓存的网络图片加载流程

缓存三

  1. 无弱引用缓存无 Lru 内存缓存有 Resource 磁盘缓存

    注意:由于缓存的已经是转换过的资源因此不需要再执行 Transform。

    缓存四

  2. 无弱引用缓存无 Lru 内存缓存无 Resource 磁盘缓存有原始图片缓存

    注意:除了不需要从网络下载,其它流程都一样。

缓存五

上述五种情况已经涵盖了差不多所有情况,不过有个问题,那就是 Lru 内存缓存的内容是哪里来的,好像没有在上面的图中有所体现?

通过翻阅代码发现 EngineResource 内部含有一个引用计数器,表示该资源现在被使用的数量。

每次调用 into() 加载一个资源,这个资源的引用计数就会加一,如果相同的资源被加载到两个不同的 Target,则在两个加载都完成后,它的引用计数将会为二,当在加载资源的 View 或 Target 上调用 clear() 或者在这个 View 或 Target 上调用对另一个资源请求的 into 方法时该资源引用计数就会减一。

// Engine.java
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
Util.assertMainThread();
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}

如果引用计数器为 0 了以后就会从弱引用缓存中移除,如果资源可以进行内存缓存那么放入 Lru 内存缓存,否则回收该资源。看到这里终于明白了,Lru 内存缓存的内容哪来的,同时 Lru 内存缓存与弱引用缓存不会存在重复资源。

那么为什么需要这个弱引用内存缓存,直接统一管理一个 Lru 内存缓存不好吗?

可能使用弱引用缓存可以杜绝被 Lru 算法删除吧。

0%