Glide源码学习随笔

Glide是什么?

Glide是一个Android的图片加载和缓存库,它主要专注于大量图片的流畅加载。是google所推荐的图片加载库,作者是bumptech。这个库被广泛运用在google的开源项目中,包括2014年的google I/O大会上发布的官方App。

简介

WIKI地址:WIKI官网

Github地址:Github

特点

1、多样化媒体加载

Glide 不仅是一个图片缓存,它支持 Gif、WebP等格式

2、生命周期集成

我们可以更加高效的使用Glide提供的方式进行绑定,这样可以更好的让加载图片的请求的生命周期动态管理起来

3、高效的缓存策略

  • 支持Memory和Disk图片缓存
  • 根据 ImageView 的大小来缓存相应大小的图片尺寸
  • 内存开销小,默认的 Bitmap 格式是 RGB_565 格式

4、 提供丰富的图片转换Api,支持圆形裁剪、平滑显示等特性

Glide怎么用?

1、gradle引入库,implementation ‘com.github.bumptech.glide:glide:4.7.1’

2、配置Glide的with load apply into等方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public void loadImageView(ImageView view,String url){
//属性的配置
RequestOptions options = new RequestOptions()
//加载成功之前占位图
.placeholder(R.mipmap.ic_launcher)
//加载错误之后的错误图
.error(R.mipmap.ic_launcher)
//指定图片的尺寸
.override(1000,800)
//指定图片的缩放类型为fitCenter (等比例缩放图片,宽或者是高等于ImageView的宽或者是高。)
.fitCenter()
//指定图片的缩放类型为centerCrop (等比例缩放图片,直到图片的狂高都大于等于ImageView的宽度,然后截取中间的显示。)
.centerCrop()
.circleCrop()//指定图片的缩放类型为centerCrop (圆形)
//跳过内存缓存
.skipMemoryCache(true)
//缓存所有版本的图像
.diskCacheStrategy(DiskCacheStrategy.ALL)
//跳过磁盘缓存
.diskCacheStrategy(DiskCacheStrategy.NONE)
//只缓存原来分辨率的图片
.diskCacheStrategy(DiskCacheStrategy.DATA)
//只缓存最终的图片
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.priority(Priority.HIGH)
;
//加载图片
Glide.with(getApplicationContext())
.load(url)
.apply(options)
.into(view);
}

3、执行ImageView的加载

1
loadImageView(ivPic,"http://b.hiphotos.baidu.com/image/pic/item/d52a2834349b033bda94010519ce36d3d439bdd5.jpg");

详细的使用教程及option的配置,推荐参考

Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

Glide核心执行流程是怎样?

基础概念

类型 说明
Data 代表原始的,未修改过的资源,对应dataClass
Resource 修改过的资源,对应resourceClass
Transcoder 资源转换器,比如BitmapBytesTranscoder(Bitmap转换为Bytes),GifDrawableBytesTranscoder
ResourceEncoder 持久化数据的接口,注意,该类并不与decoder相对应,而是用于本地缓存的接口
ResourceDecoder 数据解码器,比如ByteBufferGifDecoder(将ByteBuffer转换为Gif),StreamBitmapDecoder(Stream转换为Bitmap)
ResourceTranscoder 资源转换器,将给定的资源类型,转换为另一种资源类型,比如将Bitmap转换为Drawable,Bitmap转换为Bytes
Transformation 比如对图片进行FitCenter,CircleCrop,CenterCrop的transformation,或者根据给定宽高对Bitmap进行处理的BitmapDrawableTransformation
Target request的载体,各种资源对应的加载类,含有生命周期的回调方法,方便开发人员进行相应的准备以及资源回收工作

总体设计

image

1、构建Request,实现类为SingleRequest,用于发起一个加载的请求

2、通过EngineJob和DecodeJob负责任务创建,发起,回调,资源的管理

3、根据请求的资源类型,最后匹配对应的DateFetcher进行Data数据的获取

4、获取数据进行相应的缓存配置

5、根据原始数据Data进行解码及转换,生成最终需要显示的Resource

6、通过回调Target对应的方法,最后进行图片的显示

关键类功能说明

功能说明
Glide 向外暴露单例静态接口,构建Request,配置资源类型,缓存策略,图片处理等,可以直接通过该类完成简单的图片请求和填充。内部持有一些内存变量BitmapPool,MemoryCache,ByteArrayPool,便于低内存情况时自动清理内存
RequestManagerRetriever 用于创建RequestManager对象,并与Context做相应的生命周期绑定
RequestManagerFragment Glide向Activity或Fragment中添加的空Fragment,用于控制绑定生命周期
LifecycleListener 用于监听Activity或者Fragment的生命周期方法的接口
RequestManager 用户管理及发起请求,支持resume、pause、clear等操作
RequestBuilder 创建请求,资源类型配置,缩略图配置,以及通过BaseRequestOptions进行一些默认图,图片处理的配置
Engine 任务创建,发起,回调,管理存活和缓存的资源
EngineJob 调度DecodeJob,添加,移除资源回调,并notify回调
DecodeJob 实现了Runnable接口,调度任务的核心类,整个请求的繁重工作都在这里完成:处理来自缓存或者原始的资源,应用转换动画以及transcode。负责根据缓存类型获取不同的Generator加载数据,数据加载成功后回调DecodeJob的onDataFetcherReady方法对资源进行处理
ResourceCacheGenerator 尝试从修改过的资源缓存中获取,如果缓存未命中,尝试从DATA_CACHE中获取
DataCacheGenerator 尝试从未修改过的本地缓存中获取数据,如果缓存未命中则尝试从SourceGenerator中获取
SourceGenerator 从原始的资源中获取,可能是服务器,也可能是本地的一些原始资源
DataFetcher 数据加载接口,通过loadData加载数据并执行对应的回调
LoadPath 根据给定的数据类型的DataFetcher尝试获取数据,然后尝试通过一个或多个decodePath进行decode
DecodePath 根据指定的数据类型对resource进行decode和transcode
Registry 管理组件(数据类型+数据处理)的注册
ModelLoaderRegistry 注册所有数据加载的loader
ResourceDecoderRegistry 注册所有资源转换的decoder
TranscoderRegistry 注册所有对decoder之后进行特殊处理的transcoder
ResourceEncoderRegistry 注册所有持久化resource(处理过的资源)数据的encoder
EncoderRegistry 注册所有的持久化原始数据的encoder

代码执行流程

先贴一下流程图,建议通过源码结合流程图进行分析,下面再分步骤进行分析。
image

下面主要从with()、load()、into()3个方法进行分析。

with()

1、with()方法,最后会返回一个RequestManger对象用于发起Request。

1
2
3
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}

2、getRetriever(context),最后返回一个RequestManagerRetriever对象用于生成RequestManager。

1
2
3
4
5
6
7
8
9
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();

这里Glide.get(contenxt),会对glide进行初始化,模块扫描、组件注册等工作。

3、RequestManagerRetriever的get(context)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}

return getApplicationManager(context);
}

这里主要根据context的类型,去创建不同的RequestManager的对象,绑定生命周期。
如果context是Application对象的话,则调用,绑定了ApplicationLifecycle。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or
// activity. However, in this case since the manager attached to the application will not
// receive lifecycle events, we must force the manager to start resumed using
// ApplicationLifecycle.

// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}

4、如果context是Activity或Fragment的话,则会调用supportFragmentGet、FragmentGetd方法,创建RequestManagerFragment进行生命周期绑定ActivityFragmentLifecycle。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}

总结一下:with()方法,最后会返回一个根据context类型绑定生命周期的RequestManger对象。

load()

1.load()方法最后会生成一个RequestBuilder对象,用于构建Request,并执行请求操作.。首先会通过as方法生成一个RequestBuilder对象

1
2
3
4
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}

2、执行load()方法

1
2
3
public RequestBuilder<TranscodeType> load(@Nullable String string) {
return loadGeneric(string);
}

3、执行loadGeneric方法

1
2
3
4
5
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}

保存load传进行的参数,并设置isModelSet为true

4、设置请求的配置参数,apply(RequestOptions requestOptions)

1
2
3
4
5
public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
Preconditions.checkNotNull(requestOptions);
this.requestOptions = getMutableOptions().apply(requestOptions);
return this;
}

总结一下load()方法,最后会返回一个RequestBuilder对象,通过apply设置请求的参数,用于构建Request。

into()

into()是整个过程最复杂的一步,简单来说就是通过缓存策略及注册的Moderload,最终去加载源数据Data,并进行转换为配置的Resource,最后显示再Target上。

1、RequestBuilder的into方法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}

options = options.autoClone();
Request request = buildRequest(target, targetListener, options);

Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}

requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);

return target;
}

2、会执行BuildRequest()生成Request对象。经过一系列的调用,最后执行的方法如下,会返回一个SingleRequest对象实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private Request obtainRequest(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight) {
return SingleRequest.obtain(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListener,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}

3、接着会调用 requestManager.track(target, request);实现如下:

1
2
3
4
void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}

4、这里涉及到一个新类RequestTracker,用于管理Request的生命周期。runRequest实现如下:

1
2
3
4
5
6
7
8
9
10
11
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}

5、最后会调用Request的begin()方法开始执行请求,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public void begin() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}

if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}

if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}

// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.

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());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}

这里主要会根据status进行判断,如果COMPLETE已完成,则直接回调 onResourceReady,如果是WAITING_FOR_SIZE,则会执行onSizeReady方法,我们都知道Glide会根据实际显示的View宽高去生成最后的Resource进行显示。

6、onResourceReady实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;

float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadStatus = engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);

// This is a hack that's only useful for testing right now where loads complete synchronously
// even though under any executor running on any thread but the main thread, the load would
// have completed asynchronously.
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}

最终会通过engine的load方法去执行请求,后续的缓存策略、数据加载、图片转换都是在下面步骤执行

7、具体看Engine的load方法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb) {
Util.assertMainThread();
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;

EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);

EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}

EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}

EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}

EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);

DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);

jobs.put(key, engineJob);

engineJob.addCallback(cb);
engineJob.start(decodeJob);

if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}

这里首先通过构建EngineKey,判断内存缓存中是否命中。接着判断jobs队列中是否已存在该任务。否则会构建EngineJob、DecodeJob,并通过engineJob.start(decodeJob),通过线程池去执行DecodeJob任务。DecodeJob实现了Runnable接口,所以我们接着分析DecodeJob的run方法

8、DecodeJob的run方法最后执行了runWrapped方法,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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执行不同的任务,共两种任务类型:

runGenerators():load数据

decodeFromRetrievedData():处理已经load到的数据

RunReason再次执行任务的原因,三种枚举值:
INITIALIZE:第一次调度任务

WITCH_TO_SOURCE_SERVICE:本地缓存策略失败,尝试重新获取数据,两种情况;当stage为Stage.SOURCE,或者获取数据失败并且执行和回调发生在了不同的线程

DECODE_DATA:获取数据成功,但执行和回调不在同一线程,希望回到自己的线程去处理数据。

9、getNextStage()是获取加载资源的策略,一共5种策略:
INITIALIZE,RESOURCE_CACHE,DATA_CACHE,SOURCE,FINISHED

其中加载数据的策略有三种:
RESOURCE_CACHE,DATA_CACHE,SOURCE,
分别对应的Generator:

ResourceCacheGenerator :尝试从修改过的资源缓存中获取,如果缓存未命中,尝试从DATA_CACHE中获取

DataCacheGenerator :尝试从未修改过的本地缓存中获取数据,如果缓存未命中则尝试从SourceGenerator中获取

SourceGenerator :从原始的资源中获取,可能是服务器,也可能是本地的一些原始资源
策略的配置在DiskCacheStrategy。开发者可通过BaseRequestOptions设置:
ALL
NONE
DATA
RESOURCE
AUTOMATIC(默认方式,依赖于DataFetcher的数据源和ResourceEncoder的EncodeStrategy)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}

10、getNextGenerator,根据Stage获取到相应的Generator后会执行currentGenerator.startNext(),如果中途startNext返回true,则直接回调,否则最终会得到SOURCE的stage,重新调度任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}

// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}

11、这里我们分析最后SourceGenerator的startNext的执行,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}

if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;

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;
}

最后会通过Glide初始化时,Register注册的ModelLoader去执行对应的loadData方法,最后回调onDataFetcherReady(),获取得到DataSource,并将 runReason = RunReason.DECODE_DATA。触发调用decodeFromRetrievedData()进行源数据的转换

12、decodeFromRetrievedData,获取数据成功后,进行处理,内部调用的是runLoadPath(Data data, DataSource dataSource,LoadPath<Data, ResourceType, R> path),decode完成后的回调,对decode的资源进行transform。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
Resource<R> 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();
}
}


@SuppressWarnings("unchecked")
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}

13、decodeFromRetrievedData()中,数据decode和transform后会执行notifyEncodeAndRelease方法,在该方法中调用 notifyComplete(result, dataSource),接着调用callback.onResourceReady,实现如下:

1
2
3
4
5
6
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}

14、通过Handler将回调切换到主线程, 最后调用EngineJob的handleResultOnMainThread方法,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void handleResultOnMainThread() {
stateVerifier.throwIfRecycled();
if (isCancelled) {
resource.recycle();
release(false /*isRemovedFromQueue*/);
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;

// Hold on to resource for duration of request so we don't recycle it in the middle of
// notifying if it synchronously released by one of the callbacks.
engineResource.acquire();
listener.onEngineJobComplete(this, key, engineResource);

//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource, dataSource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();

release(false /*isRemovedFromQueue*/);
}

15、进行相关的资源清理后,最后调用SingleRequest的onResourceReady(Resource<?> resource, DataSource dataSource) 方法,最后调用target.onResourceReady(result, animation)方法,实现资源的显示,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;

if (glideContext.getLogLevel() <= Log.DEBUG) {
Log.d(GLIDE_TAG, "Finished loading " + result.getClass().getSimpleName() + " from "
+ dataSource + " for " + model + " with size [" + width + "x" + height + "] in "
+ LogTime.getElapsedMillis(startTime) + " ms");
}

isCallingCallbacks = true;
try {
if ((requestListener == null
|| !requestListener.onResourceReady(result, model, target, dataSource, isFirstResource))
&& (targetListener == null
|| !targetListener.onResourceReady(result, model, target, dataSource, isFirstResource))) {
Transition<? super R> animation =
animationFactory.build(dataSource, isFirstResource);
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}

notifyLoadSuccess();
}

Glide是如何与Activity及Fragment等的生命周期绑定?

Glide在执行with的阶段,会根据context的类型,将Glide的Request请求与context类型进行绑定。Application类型为整个应用的生命周期。Fragment及Activity类型,通过巧妙的设计一个RequestManagerFragment,加入到Activity或Fragment当中,从而实现生命周期的监听。

1、Application的绑定为ApplicationLifecycle,与App的生命周期一致

1
2
3
4
5
6
7
8
9
 @Override
public void addListener(@NonNull LifecycleListener listener) {
listener.onStart();
}

@Override
public void removeListener(@NonNull LifecycleListener listener) {
// Do nothing.
}

2、Activity或Fragment的绑定为ActivityFragmentLifecycle,与宿主对应的生命周期一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface LifecycleListener {

/**
* Callback for when {@link android.app.Fragment#onStart()}} or {@link
* android.app.Activity#onStart()} is called.
*/
void onStart();

/**
* Callback for when {@link android.app.Fragment#onStop()}} or {@link
* android.app.Activity#onStop()}} is called.
*/
void onStop();

/**
* Callback for when {@link android.app.Fragment#onDestroy()}} or {@link
* android.app.Activity#onDestroy()} is called.
*/
void onDestroy();
}

3、在RequestManger中实现了监听接口的注册,代码如下:

1
2
3
4
5
6
private final Runnable addSelfToLifecycle = new Runnable() {
@Override
public void run() {
lifecycle.addListener(RequestManager.this);
}
};

这里注意,一个页面拥有一个RequestManagerFragment,RequestManagerFragment会持有RequestManger的引用。一个页面发起多个Glide显示图片请求,会优先从Fragment中获取RequestManger,不会重复创建多个RequestManger对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}

4、RequestManger中绑定的回调执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* Lifecycle callback that registers for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and restarts failed or paused
* requests.
*/
@Override
public void onStart() {
resumeRequests();
targetTracker.onStart();
}

/**
* Lifecycle callback that unregisters for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
*/
@Override
public void onStop() {
pauseRequests();
targetTracker.onStop();
}

/**
* Lifecycle callback that cancels all in progress requests and clears and recycles resources for
* all completed requests.
*/
@Override
public void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}

从实现可知,当Activity或Fragment退到后台时,会调用pauseRequests()暂停请求,回到前台时会重新执行请求,当页面销毁时,会进行对应的资源清理及回收。

image

Glide的缓存实现原理是怎样的?

简介

image

Glide的缓存使用内存缓存及硬盘缓存进行处理。

缓存 说明
ActiveResources ActiveResources是一个以弱引用资源为value。用于缓存正在使用的资源
MemoryCache MemoryCache是使用LruResourceCache实现,用于缓存非正在使用的资源
DiskCache 进行资源磁盘缓存
Http 通过网络地址,从服务端加载资源文件

假如用户配置了使用内存缓存及磁盘缓存,则主要的加载实现流程如下:

1、当发起Request时,首先会从ActiveResources中进行缓存查找。如果命中则返回显示,如果不命中,则从MemoryCache中获取。当资源从ActiveResources中移除后,加入到MemoryCache中

2、当在MemoryCache中命中时,则会将资源加入到ActiveResources中,并在该Cache中移除,如果不命中则会尝试从磁盘缓存中进行加载

3、根据用于配置的策略,如果在磁盘缓存中命中,则会返回,并将资源缓存到ActiveResources当中,如果不命中,则会将进行网络的请求

4、根据ModelLoader的配置实现,从网络中加载资源,并根据配置,缓存到磁盘及内存缓存中

Key

根据流程分析,我们知道Key的生成在Engine的load方法中,具体的实现如下:

1
2
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);

可见为了兼容复杂的资源转换,保证key的唯一性,包含了非常多的参数进行构建。主要有model(目标地址)、signature(设置的签名)、图片的width、heigh、资源的转换配置等。

内存缓存

根据上面的简介,我们可以知道,Glide主要的内存缓存策略采用了2级缓存,为ActiveResources和MemoryCache。下面我们从源码的角度分析这2个缓存的机制。

ActiveResources

1、 根据源码,我们可知内存采用了一个HashMap进行内存的缓存,使用了弱引用ResourceWeakReference持有了Resource

1
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();

2、当获取资源时,主要采用get方法进行获取,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
EngineResource<?> get(Key key) {
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null;
}

EngineResource<?> active = activeRef.get();
if (active == null) {
cleanupActiveReference(activeRef);
}
return active;
}

如果命中,就返回资源。这里注意,如果当active==null,引用被回收时,会调用cleanupActiveReference方法,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
Util.assertMainThread();
activeEngineResources.remove(ref.key);

if (!ref.isCacheable || ref.resource == null) {
return;
}
EngineResource<?> newResource =
new EngineResource<>(ref.resource, /*isCacheable=*/ true, /*isRecyclable=*/ false);
newResource.setResourceListener(ref.key, listener);
listener.onResourceReleased(ref.key, newResource);
}

如果ref.resource!=null,则会重新生成一个EngineResource对象,并回调onResourceReleased方法,我们看具体的实现如下:

1
2
3
4
5
6
7
8
9
10
@Override
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
Util.assertMainThread();
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}

从源码可知,会调用deactivate方法,从activeResources中移除,然后加入到MemoryCache中。

3、写入资源

1
2
3
4
5
6
7
8
9
10
11
12
13
void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key,
resource,
getReferenceQueue(),
isActiveResourceRetentionAllowed);

ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}

EngineResource

EngineResource中主要为了一个acquired变量

1
private int acquired;

当资源被使用时,会调用acquire,将变量值+1

1
2
3
4
5
6
7
8
9
void acquire() {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
if (!Looper.getMainLooper().equals(Looper.myLooper())) {
throw new IllegalThreadStateException("Must call acquire on the main thread");
}
++acquired;
}

当资源被释放时,会调用release()方法

1
2
3
4
5
6
7
8
9
10
11
void release() {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (!Looper.getMainLooper().equals(Looper.myLooper())) {
throw new IllegalThreadStateException("Must call release on the main thread");
}
if (--acquired == 0) {
listener.onResourceReleased(key, this);
}
}

这里注意,当acquired == 0,表明资源没有被使用时,则会调用onResourceReleased,将资源存储到MemoryCache中。这样也就实现了正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存的功能。

MemoryCache

Glide在Build的过程中会创建具体的MemoryCache对象,具体的实现如下:

1
2
3
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}

从源码可知,MemoryCache的主要实现是采用了LRU算法,我们具体查看LruResourceCache的实现。发现其继承与LruCache,LruCache的关键实现如下:

1
private final Map<T, Y> cache = new LinkedHashMap<>(100, 0.75f, true);

从源码可知,Glide的内存缓存的LRU算法实现主要是使用了LinkedHashMap。

这里详细的说明可参考:
如何用LinkedHashMap实现LRU缓存算法

总结一下

glide采用了2级的内存缓存,activeResources是一个以弱引用资源为value,的map,memory是使用LruResourceCache实现的。就是activeResources是一个随时有可能被回收资源。它存在的意义在于,memory的强引用的频繁读写也有可能造成内存激增频繁GC,而造成内存抖动。资源在使用的过程中将会保存在activeResources中,而activeResources是弱引用的,可以随时被系统回收,不会造成内存泄漏和过多的使用

硬盘缓存

缓存策略

Glide缓存的资源分为两种(1,原图(SOURCE)原始图片 2,处理图(RESULT)经过压缩和变形等转化的图片)

硬盘缓存分为五种,具体看一面。可以通过调用diskCacheStrategy()方法并传入五种不同的参数

1,DiskCacheStrategy.NONE// 表示不缓存任何内容

2,DiskCacheStrategy.DATA// 表示只缓存原始图片

3,DiskCacheStrategy.RESOURCE// 表示只缓存转换过后的图片

4,DiskCacheStrategy.ALL // 表示既缓存原始图片,也缓存转换过后的图片

5,DiskCacheStrategy.AUTOMATIC//表示让Glide根据图片资源智能地选择使用哪一种缓存策略(默认选项)

缓存的获取

1、根据上述的流程分析,我们知道具体磁盘缓存在DecodeJob中执行,当任务开始时,调用了runWrapped()方法,接着会调用getNextStage,这里会获取磁盘的加载策略Stage,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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:
// Skip loading from source if the user opted to only retrieve the resource from cache.
return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}

接着会调用getNextGenerator方法,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}
}

这里获取下一步执行的策略,一共5种策略:
INITIALIZE,RESOURCE_CACHE,DATA_CACHE,SOURCE,FINISHED

其中加载数据的策略有三种:
RESOURCE_CACHE,DATA_CACHE,SOURCE,
分别对应的Generator:

ResourceCacheGenerator :尝试从修改过的资源缓存中获取,如果缓存未命中,尝试从DATA_CACHE中获取

DataCacheGenerator :尝试从未修改过的本地缓存中获取数据,如果缓存未命中则尝试从SourceGenerator中获取

SourceGenerator :从原始的资源中获取,可能是服务器,也可能是本地的一些原始资源

接着调用具体的Generator的startNext方法。磁盘的缓存获取实现在这个方法中获取。
这里ResourceCacheGenerator的关键缓存获取代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = helper.getTransformation(resourceClass);
// PMD.AvoidInstantiatingObjectsInLoops Each iteration is comparatively expensive anyway,
// we only run until the first one succeeds, the loop runs for only a limited
// number of iterations on the order of 10-20 in the worst case.
currentKey =
new ResourceCacheKey(// NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}

DataCacheGenerator的关键缓存获取代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
Key sourceId = cacheKeys.get(sourceIdIndex);
// PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
// and the actions it performs are much more expensive than a single allocation.
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}

缓存的写入

1、Data数据的缓存

从服务端获取数据的主要实现在SourceGenerator中,我们查看源码onDataReady可知其中判断了isDataCacheable()会将数据赋值到dataToCache中。重新触发reschedule();

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}

当再次出发startNext,关键实现如下:

1
2
3
4
5
6
7
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}

其中的cacheData,将原始的Data数据缓存到磁盘文件中,代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
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);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished encoding source to cache"
+ ", key: " + originalKey
+ ", data: " + dataToCache
+ ", encoder: " + encoder
+ ", duration: " + LogTime.getElapsedMillis(startTime));
}
} finally {
loadData.fetcher.cleanup();
}

sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}

2、Resource数据的缓存

根据上述的流程分析,再DecodeJob中的onResourceDecoded回调中,关键的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource,
encodeStrategy)) {
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<Z> lockedResult = LockedResource.obtain(transformed);
deferredEncodeManager.init(key, encoder, lockedResult);
result = lockedResult;
}

会初始化DeferredEncodeManager对象。接着会执行到notifyEncodeAndRelease()方法,其中关键的实现如下:

1
2
3
4
5
6
7
8
9
try {
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}

在encode中进行了resource数据的缓存,代码如下:

1
2
3
4
5
6
7
8
9
10
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
diskCacheProvider.getDiskCache().put(key,
new DataCacheWriter<>(encoder, toEncode, options));
} finally {
toEncode.unlock();
GlideTrace.endSection();
}
}

缓存的实现

1、在Glide的Build方法中,我们可以看到磁盘缓存的工厂实例,代码如下:

1
2
3
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}

2、InternalCacheDiskCacheFactory继承了DiskLruCacheFactory,工厂关键的build方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public DiskCache build() {
File cacheDir = cacheDirectoryGetter.getCacheDirectory();

if (cacheDir == null) {
return null;
}

if (!cacheDir.mkdirs() && (!cacheDir.exists() || !cacheDir.isDirectory())) {
return null;
}

return DiskLruCacheWrapper.create(cacheDir, diskCacheSize);
}

3、可见实际的磁盘缓存对象为DiskLruCacheWrapper类,我们看对应的get、put方法,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
@Override
public File get(Key key) {
String safeKey = safeKeyGenerator.getSafeKey(key);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Get: Obtained: " + safeKey + " for for Key: " + key);
}
File result = null;
try {
// It is possible that the there will be a put in between these two gets. If so that shouldn't
// be a problem because we will always put the same value at the same key so our input streams
// will still represent the same data.
final DiskLruCache.Value value = getDiskCache().get(safeKey);
if (value != null) {
result = value.getFile(0);
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Unable to get from disk cache", e);
}
}
return result;
}

@Override
public void put(Key key, Writer writer) {
// We want to make sure that puts block so that data is available when put completes. We may
// actually not write any data if we find that data is written by the time we acquire the lock.
String safeKey = safeKeyGenerator.getSafeKey(key);
writeLocker.acquire(safeKey);
try {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Put: Obtained: " + safeKey + " for for Key: " + key);
}
try {
// We assume we only need to put once, so if data was written while we were trying to get
// the lock, we can simply abort.
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(safeKey);
}
}

4、最终可知磁盘缓存的实现为DiskLruCache,关于DiskLruCache缓存可参考如下博文

DiskLruCache缓存

Glide的底层网络实现是什么?

通过上述的流程分析,我们可知最后的网络加载在SourceGenerator中的startNext()方法,通过初始注册的ModelLoader对应的DataFetcher去加载数据。我们以load一个GlideUrl地址来分析

在Glide的构造函数中,Register会注册ModelLoader,代码实现如下:

1
.append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())

在SourceGenerator中的startNext()方法中会循环匹配出对应的ModelLoader,实现如下:

1
2
3
4
5
6
7
8
9
10
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);
}
}

我们查看HttpGlideUrlLoader,其最后的LoadData实现为HttpUrlFetcher,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
@NonNull Options options) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
// spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}

HttpUrlFetcher具体加载网络的数据的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");

}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}

urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);

// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);

// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}

通过分析可知,Glide默认的网络加载使用的是urlConnection。当然我们也可以通过自定义ModelLoader,使用okhttp、volley等的网络框架进行加载。

具体可参考博文
Glide 4.x添加自定义组件原理

Glide中代码运用了那些设计模式,有什么巧妙的设计?

1、建造者模式

Glide对象的创建使用Build模式,将复杂对象的创建和表示分离,调用者不需要知道复杂的创建过程,使用Build的相关方法进行配置创建对象。

2、外观模式

Glide对外提供了统一的调度,屏蔽了内部的实现,使得使用该网络库简单便捷。

3、策略模式

关于DecodeJob中的DataFetcherGenerator资源获取,采用了策略模式,将数据加载的不同算法进行封装。

4、工厂模式

ModelLoader的创建使用了ModelLoaderFactory、Engine中的EngineJobFactory、DiskLruCacheFactory等

总结

思考

Glide正因为其强大的功能,高效的运行机制,所以源码的实现非常复杂。在学习的过程中也遇到很多的困难,最终还是一步一步坚持下来了。有时候放弃就是一瞬间的念头,但坚持下来,终究会有收获。

参考资料

Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

如何用LinkedHashMap实现LRU缓存算法

DiskLruCache缓存

Glide解析-cache

Glide 4.x添加自定义组件原理

Android Glide源码分析