Glide 源码解析

在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech。这个库被广泛的运用在google的开源项目中,包括2014年google I/O大会上发布的官方app。Glide和Picasso使用上有90%的相似度,但是内部实现机制有很大区别 Glide介绍

1.主要特点

(1)支持Memory和Disk图片缓存。
(2)支持gif和webp格式图片。
(3)根据Activity/Fragment生命周期自动管理请求。
(4)使用Bitmap Pool可以使Bitmap复用。
(5)对于回收的Bitmap会主动调用recycle,减小系统回收压力。

2. 总体设计

总体设计图

基本概念

RequestManager:请求管理,每一个Activity都会创建一个RequestManager,根据对应Activity的生命周期管理该Activity上所以的图片请求。

Engine:加载图片的引擎,根据Request创建EngineJob和DecodeJob。

EngineJob:图片加载。

DecodeJob:图片处理。

流程图

这里是大概的总体流程图, 具体的细节中流程下面继续分析。
总体流程图

3. 核心类介绍

3.1 Gilde
用于保存整个框架中的配置。

重要方法:

1
2
3
4
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}

用于创建RequestManager,这里是Glide通过Activity/Fragment生命周期管理Request原理所在,这个类很关键、很关键、很关键,重要的事情我只说三遍。
主要原理是创建一个自定义Fragment,然后通过自定义Fragment生命周期操作RequestManager,从而达到管理Request。

RequestManager创建流程图

3.2 RequestManagerRetriever

1
2
3
4
5
6
7
8
9
RequestManager supportFragmentGet(Context context, FragmentManager fm) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}

这里判断是否只当前RequestManagerFragment是否存在RequestManager,保证一个Activity对应一个RequestManager, 这样有利于管理一个Activity上所有的Request。创建RequestManager的时候会将RequestManagerFragment中的回调接口赋值给RequestManager,达到RequestManager监听RequestManagerFragment的生命周期。

3.3 RequestManager
成员变量:
(1)Lifecycle lifecycle,用于监听RequestManagerFragment生命周期。
(2)RequestTracker requestTracker, 用于保存当前RequestManager所有的请求和带处理的请求。

重要方法:

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
@Override
//开始暂停的请求
public void onStart() {
resumeRequests();
}
//停止所有的请求
@Override
public void onStop() {
pauseRequests();
}

//关闭所以的请求
@Override
public void onDestroy() {
requestTracker.clearRequests();
}

//创建RequestBuild
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}

public <Y extends Target<TranscodeType>> Y into(Y target) {
...
Request previous = target.getRequest();
//停止当前target中的Request。
if (previous != null) {
previous.clear(); //这个地方很关键,见Request解析
requestTracker.removeRequest(previous);
previous.recycle();
}
...
return target;
}

3.4 DrawableRequestBuilder
用于创建Request。 这里面包括很多方法,主要是配置加载图片的url、大小、动画、ImageView对象、自定义图片处理接口等。

3.5 Request
主要是操作请求,方法都很简单。

1
2
3
4
5
6
7
8
9
@Override
public void clear() {
...
if (resource != null) {
//这里会释放资源
releaseResource(resource);
}
...
}

这里的基本原理是当有Target使用Resource(Resource见下文)时,Resource中的引用记数值会加一,当释放资源Resource中的引用记数值减一。当没有Target使用的时候就会释放资源,放进Lrucache中。

3.7 EngineResource
实现Resource接口,使用装饰模式,里面包含实际的Resource对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void release() {
if (--acquired == 0) {
listener.onResourceReleased(key, this);
}
}

void acquire() {
++acquired;
}

@Override
public void recycle() {
isRecycled = true;
resource.recycle();
}

acquire和release两个方法是对资源引用计数;recycle释放资源,一般在Lrucache饱和时会触发。

3.8 Engine(重要)
请求引擎,主要做请求的开始的初始化。

3.8.1 load方法
这个方法很长,将分为几步分析

(1)获取MemoryCache中缓存
首先创建当前Request的缓存key,通过key值从MemoryCache中获取缓存,判断缓存是否存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
....
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
return cached;
}

@SuppressWarnings("unchecked")
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);

final EngineResource result;
...
return result;
}

(重点)从缓存中获取的时候使用的cache.remove(key),然后将值保存在activeResources中,然后将Resource的引用计数加一。
优点:

正使用的Resource将会在activeResources中,不会出现在cache中,当BitmapPool中缓存饱和的时候或者系统内存不足的时候,清理Bitmap可以直接调用recycle,不用考虑Bitmap正在使用导致异常,加快系统的回收。


(2)获取activeResources中缓存
activeResources通过弱引用保存recouse ,也是通过key获取缓存,

1
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable)

(3)判断当前的请求任务是否已经存在

1
2
3
4
5
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
return new LoadStatus(cb, current);
}

如果任务请求已经存在,直接将回调事件传递给已经存在的EngineJob,用于请求成功后触发回调。


(4)执行请求任务

1
2
3
4
5
6
7
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);

3.9 EngineRunnable
请求执行Runnable,主要功能请求资源、处理资源、缓存资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 private Resource<?> decodeFromCache() throws Exception {
Resource<?> result = null;
try {
result = decodeJob.decodeResultFromCache();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Exception decoding result from cache: " + e);
}
}

if (result == null) {
result = decodeJob.decodeSourceFromCache();
}
return result;
}

private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}

加载DiskCache和网络资源。加载DiskCache包括两个,因为Glide默认是保存处理后的资源(压缩和裁剪后),缓存方式可以自定义配置。如果客户端规范设计,ImageView大小大部分相同可以节省图片加载时间和Disk资源。

3.10 DecodeJob

1
2
3
public Resource<Z> decodeResultFromCache() throws Exception  

public Resource<Z> decodeSourceFromCache() throws Exception

从缓存中获取处理后的资源。上面有关Key的内容,Key是一个对象,可以获取key和orginKey。decodeResultFromCache就是通过key获取缓存,decodeSourceFromCache()就是通过orginKey获取缓存。

1
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded)

处理和包装资源;缓存资源。

1
2
3
4
//保存原资源
private Resource<T> cacheAndDecodeSourceData(A data) throws IOException
//保存处理后的资源
private void writeTransformedToCache(Resource<T> transformed)

3.11 Transformation

1
Resource<T> transform(Resource<T> resource, int outWidth, int outHeight);

处理资源,这里面会从BitmapPool中获取缓存Bitmap,如果有的话直接将缓存的Bitmap当成画布,如果达到Bitmap复用, 对于处理前的资源直接回收到 BitmapPool中。

3.12 ResourceDecoder
用于将文件、IO流转化为Resource

3.13BitmapPool
用于存放从LruCache中remove的Bitmap, 用于后面创建Bitmap时候的重复利用。

4.杂谈

Glide的架构扩展性高,但是难以理解,各种接口、泛型,需要一定的学习才能熟练运用。

Glide的优点:

(1)支持对处理后的资源DiskCache。
(2)支持inBitmap高级特性(详情Google)。
(3)使用巧妙的设计:使用BitmapPool减少内存抖动;通过弱引用activityResources缓存正在使用的resource,而不是在MemoryCache,在内存不足的时候,MemoryCache中的图片会移除到itmapPool中,对于BitmapPool饱和移除的Bitmap可以直接调用recycle。