JasonWang's Blog

Glide详解之图片加载过程分析

字数统计: 2.9k阅读时长: 14 min
2017/05/30

在之前的一篇文章(Glide架构分析 )中介绍了Glide的具体原理。这篇文章,用一个下载图片的示例来说明Glide加载图片的整个过程。以下是用Glide从网络上加载一个图片的代码片段:

1
2
3
4
5
6
7
8
9
    
Glide
.with(activity) // Activity
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.crossFade()
.into(myImageView);

大致说来,在Glide中图片的加载有如下几个过程:

  • 产生一个图片加载的请求GenericRequest<T>;
  • 将图片加载请求发送给资源引擎中心Engine,由其负责资源的加载以及数据解码任务的管理;
  • EngineRunnable首先尝试从DISK加载资源,完成后进行回调,将数据加载到ImageView中;
  • 磁盘中没有请求对应的资源,则尝试从网络端下载,并解码完成后回调,将数据加载到Target对象;

现在就来详细的看下Glide加载图片的整个过程。

产生图片加载请求

调用Glide.with(activity)产生一个RequestManager,用来负责管理和启动数据加载请求。

1
2
3
4
5
6

public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}

RequestManagerRetriever产生RequestMananger时,会启动一个没有视图对象的Fragment用以自动管理Glide中加载请求的生命周期:

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

RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}


继续调用RequestManager.load(url),RequestMananger开始着手创建新的图片加载请求DrawableTypeRequest<String>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22


public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}

public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}

private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
// 数据加载器,Glide默认的是HttpUrlGlideUrlLoader
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
....

return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}

通过DrawableTypeRequest,可以配置图片加载时的参数,如图片显示位置(fitCenter;centerCrop),图片加载的优先级priority,设置下载之前的占位符图片placeholder(),以及图片下载失败时的图片error():

这些参数配置功能均在DrawableRequestBuilder

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

@Override
public DrawableRequestBuilder<ModelType> priority(Priority priority) {
super.priority(priority);
return this;
}

@SuppressWarnings("unchecked")
public DrawableRequestBuilder<ModelType> centerCrop() {
return transform(glide.getDrawableCenterCrop());
}

@SuppressWarnings("unchecked")
public DrawableRequestBuilder<ModelType> fitCenter() {
return transform(glide.getDrawableFitCenter());
}

public final DrawableRequestBuilder<ModelType> crossFade() {
super.animate(new DrawableCrossFadeFactory<GlideDrawable>());
return this;
}

@Override
public DrawableRequestBuilder<ModelType> error(int resourceId) {
super.error(resourceId);
return this;
}

至此,图片加载请求就完成了。此时调用into(ImageView),Glide就可以准备加载图片了。在GenericRequestBuilder中,调用into(ImageView)时首先会将ImageView包装成一个Target<R>,这里的资源类型RGlideDrawable:

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

public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}

if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}

return into(glide.buildImageViewTarget(view, transcodeClass));
}

Glide调用ImageViewTargetFactory来生成一个Target对象,这里实际创建的是一个GlideDrawableImageViewTarget对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public class ImageViewTargetFactory {

@SuppressWarnings("unchecked")
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}

调用into(Target),在这里生成一个Request,并将其传递给RequestTracker执行。

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

public <Y extends Target<TranscodeType>> Y into(Y target) {
Util.assertMainThread();
....
Request previous = target.getRequest();

if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
// 产生图片加载请求
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);

return target;
}

启动图片加载

调用RequestTracker.runRequest,保存该Request到队列中,如果当前状态没有处于paused,则直接调用Request.begin,启动Request:

1
2
3
4
5
6
7
8
9
10
11


public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}

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

@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}

status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}

if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
}

如果图片给定的尺寸大小有效,则从FixedLoadProvider中获取一个数据加载器(在构造DrawableTypeRequest时由Glide创建),并将其作为参数传入Engine进行图片加载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

@Override
public void onSizeReady(int width, int height) {
....
status = Status.RUNNING;

width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);

ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
....
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
....
loadedFromMemoryCache = true;
// 调用load进行图片加载
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;

}

加载图片,如果缓存中有,则直接回调返回该资源;或者Engine中有保存的活跃资源(之前提交了Request但尚未被释放的资源)也会直接返回该资源。否则需要从DISK或者网络端进行加载:

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


public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();

final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
// cache中已经包含
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
// 活跃资源中包含该请求所需资源
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}

EngineJob current = jobs.get(key);
....
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负责图片下载,解码
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
// 开始执行图片加载
engineJob.start(runnable);

return new LoadStatus(cb, engineJob);
}


EngineRunnable可执行对象放入一个优先级线程池FifoPriorityThreadPoolExecutor中执行:

两个线程池均在GlideBuilder创建,然后传给Engine的构造函数

1
2
3
4
5
6

public void start(EngineRunnable engineRunnable) {
this.engineRunnable = engineRunnable;
future = diskCacheService.submit(engineRunnable);
}

由于EngineRunnable的初始状态为CACHE,因此首先会尝试从DISK的缓存中加载资源,如果加载成功,则直接回调返回该资源给上层调用者:

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

@Override
public void run() {
....
Exception exception = null;
Resource<?> resource = null;
try {
// 从DISK或者NETWORK加载图片
resource = decode();
} catch (Exception e) {
exception = e;
}
// 资源加载结束,回调返回结果
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}


private Resource<?> decode() throws Exception {
// stage为cache,直接从cache中获取资源
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}

调用DecodeJob.decodeFromCache()尝试从CACHE中获取资源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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

如果CACHE中并没有发现可用的资源,EngineRunnable进入SOURCE状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

private void onLoadFailed(Exception e) {
if (isDecodingFromCache()) {
stage = Stage.SOURCE;
// 通过EngineJob将该执行对象再次执行
manager.submitForSource(this);
} else {
manager.onException(e);
}
}

// EngineJob: 这一次提交到网络端线程池加载资源
@Override
public void submitForSource(EngineRunnable runnable) {
future = sourceService.submit(runnable);
}


从网络端下载图片,回调返回结果

调用DecodeJob.decodeFromSource(),从网络端下载图片,并将数据解码成对应格式:

1
2
3
4
5
6
7

public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}


调用Resource<T>对应的DataFetcher<A>来从网络端加载数据,这里T是一个Bitmap, 而A是一个InputStream数据流;接着讲该输入流转换成相应的Resource<Bitmap>类:

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

private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();
// 这里的DataFetcher实际上是OkHttpStreamFetcher
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}

调用OkHttpStreamFetcher.loadData()加载数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

@Override
public InputStream loadData(Priority priority) throws Exception {
Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());

for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();

Response response;
call = client.newCall(request);
response = call.execute();
responseBody = response.body();
if (!response.isSuccessful()) {
throw new IOException("Request failed with code: " + response.code());
}

long contentLength = responseBody.contentLength();
stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
return stream;
}

这里loadProviderStreamBitmapDataLoadProvider,而用于资源解码的是StreamBitmapDecoder

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

private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;
// 默认的磁盘缓存策略是(source,result) = (false, true)
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
// 调用InputStream对应的资源解码器对InputStream进行解密
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
}
return decoded;
}

//StreamBitmapDecoder: 输入流数据进行解码,按照给定的图片尺寸对图片进行比例采样
@Override
public Resource<Bitmap> decode(InputStream source, int width, int height) {
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
return BitmapResource.obtain(bitmap, bitmapPool);
}

至此图片解码完成了,接着要讲Resource<Bitmap>转换成另一种包装类格式GlideBitmapDrawable,因而此时实际返回的是一个GlideBitmapDrawableResource:

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

public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
// 大小变换以及转换成另一个类Z
return transformEncodeAndTranscode(decoded);
}

private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
long startTime = LogTime.getLogTime();
Resource<T> transformed = transform(decoded);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transformed resource from source", startTime);
}
// 将变换后的结果保存到缓存
writeTransformedToCache(transformed);
// 转换成GlideBitmapDrawable
Resource<Z> result = transcode(transformed);

return result;
}

回调onLoadComplete(),告知EngineJob资源加载完成了:

1
2
3
4
5

private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}

回调EngineJob.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

class EngineJob implements EngineRunnable.EngineRunnableManager {
// 用于向主线程发送消息
private static final Handler MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper(), new MainThreadCallback());

@Override
public void onResourceReady(final Resource<?> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}

private static class MainThreadCallback implements Handler.Callback {

@Override
public boolean handleMessage(Message message) {
if (MSG_COMPLETE == message.what || MSG_EXCEPTION == message.what) {
EngineJob job = (EngineJob) message.obj;
if (MSG_COMPLETE == message.what) {
// 资源加载成功
job.handleResultOnMainThread();
} else {
job.handleExceptionOnMainThread();
}
return true;
}

return false;
}
}
}

在主线程上处理结果:首先告知Engine资源加载完毕,接着告知所有ResourceCallback,并返回一个含有资源的包装类EngineResource:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

private void handleResultOnMainThread() {

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(key, engineResource);
// 回调接口在Engine中添加
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
}


回忆下最开始调用Engine.decode()的地方GenericRequest,可以看到,ResourceCallback的源头在GenericRequest

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

@SuppressWarnings("unchecked")
@Override
public void onResourceReady(Resource<?> resource) {
if (resource == null) {
onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
+ " inside, but instead got null."));
return;
}
....
onResourceReady(resource, (R) received);
}

private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
// RequestListener是用户自己定义的监听接口
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
// 告知Target资源加载完毕
target.onResourceReady(result, animation);
}

notifyLoadSuccess();
}

调用ImageViewTarget.onResourceReady,将资源加载到ImageView:

1
2
3
4
5
6
7
8

@Override
public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
setResource(resource);
}
}

至此这个图片就加载完成了。相比Piccasso,Glide大量使用了泛型,所以看代码可能比较费力,但是只要搞清楚各个泛型之间的关系,代码之间的逻辑就容易理清了。

原文作者:Jason Wang

更新日期:2022-03-22, 20:10:01

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG
  1. 1. 产生图片加载请求
  2. 2. 启动图片加载
  3. 3. 从网络端下载图片,回调返回结果