源码分析-glide

  • 支持GIF,webP ,video
  • 生命周期集成
  • 高效缓存,根据imageView尺寸进行缓存
  • 默认 RGB_565内存开销比较小

RequestManager获取

通过Glide.with() 获取RequestManager对象,为每个activity对应添加一个透明不可见的fragment,并且持有当前fragment的生命周期引用,将图片加载的工作同生命周期进行绑定 。

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

DrawableTypeRequest

1
2
3
4
5
6
7
8
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}

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

返回DrawableTypeRequest构造

  • modelClass : java.lang.String
  • streamModelLoader : StreamStringLoader
  • fileDescriptorModelLoader : FileDescriptorStringLoader

DrawableTypeRequest#into

GenericRequestBuilder#into

1
2
3
4
public Target<TranscodeType> into(ImageView view) {
.......
return into(glide.buildImageViewTarget(view, transcodeClass));
}

glide.buildImageViewTarget = DrawableImageViewTarget

GenericRequestBuilder#into

1
2
3
4
5
6
7
8
9
//target: ImageView
public <Y extends Target<TranscodeType>> Y into(Y target) {
Request request = buildRequest(target); // GenericRequest
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);

return target;
}

RequestTracker#runRequest

1
2
3
4
5
6
7
8
9
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}

GenericRequest#begin

1
2
3
4
5
6
7
8
9
10
public void begin() {

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

ViewTarget#getSize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
} else {
// We want to notify callbacks in the order they were added and we only expect one or two callbacks to
// be added a time, so a List is a reasonable choice.
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
final ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}

GenericRequest#onSizeReady

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
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;

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


// 1. ImageVideoModelLoader
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();

// 2. ImageVideoModelLoader#ImageVideoFetcher
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);

if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
//3.
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}

Engine#load

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public <T, Z, R> LoadStatus load(){

final String id = fetcher.getId();//1. imageUrl
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());

·····

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)// cb: GenericRequest
engineJob.start(runnable);

}

EngineRunnable

1
2
3
4
5
6
7
8
9
10
11
public void run() {
Resource<?> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
}
1
2
3
4
5
6
7
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
1
2
3
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}

DecodeJob

1
2
3
4
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();

// 1, fetcher: ImageVideoModelLoader

final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
//2,
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}

ImageVideoModelLoader

1
2
3
4
5
6
public ImageVideoWrapper loadData(Priority priority) throws Exception {
// 1. streamFetcher: HttpUrlFetcher
is = streamFetcher.loadData(priority);

return new ImageVideoWrapper(is, fileDescriptor);
}

HttpUrlFetcher

1
2
3
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
1
2
3
4
5
6
7
8
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
throws IOException {
//http requestt ....
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}

获取到结果:ImageVideoWrapper , 进入 DecodeJob

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
// 2.
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
1
2
3
4
5
6
7
8
9
private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;

long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);


return decoded;
}

GIfBitmapWrapperResourceDecoder

1
2
3
4
5
6
7
8
9
10
11
12
13
public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();

GifBitmapWrapper wrapper = null;
try {
// 1.
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
}
1
2
3
4
5
6
7
private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException {
final GifBitmapWrapper result;
if (source.getStream() != null) {
result = decodeStream(source, width, height, bytes);
}
return result;
}
1
2
3
4
5
6
private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes){

ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());
result = decodeBitmapWrapper(forBitmapDecoder, width, height);
return result;
}
1
2
3
4
5
6
7
8
9
10
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;

Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height);
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
}

return result;
}

ImageVideoBitmapDecoder

1
2
3
4
5
public Resource<Bitmap> decode(ImageVideoWrapper source, int width, int height){
result = streamDecoder.decode(is, width, height);

return result;
}

StreamBitmapDecoder

1
2
3
4
public Resource<Bitmap> decode(InputStream source, int width, int height) {
Bitmap bitmap = downsampler.decode(source, bitmapPool, width, height, decodeFormat);
return BitmapResource.obtain(bitmap, bitmapPool);
}

DownSamper

1
2
3
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
·······
}

流转到 EngineRunnable

1
2
3
4
5
6
7
8
9
10
11
public void run() {
······

resource = decode();

if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
1
2
3
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}

EngineJob

1
2
3
4
public void onResourceReady(final Resource<?> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
handler
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();
}
return true;
}

return false;
}

1
2
3
4
5
6
7
8
private void handleResultOnMainThread() {
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
}

GenericRequest

1
2
3
4
public void onResourceReady(Resource<?> resource) {
onResourceReady(resource, (R) received);
}

1
2
3
4
5
private void onResourceReady(Resource<?> resource, GlideBitmapDrawable result) {
status = Status.COMPLETE;
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
target.onResourceReady(result, animation);
}

GlideDrawwableImageViewTarget

1
2
3
4
5
6
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
super.onResourceReady(resource, animation);//1.
this.resource = resource;
resource.setLoopCount(maxLoopCount);
resource.start();
}
1
2
3
protected void setResource(GlideDrawable resource) {
view.setImageDrawable(resource);
}

一阶段: 图片加载成功 。。。。。。。。

glide对于图片的加载分为以下几个步骤完成

  1. 构造request
  2. cache/网络请求进行图片获取
  3. decode/图片解析

图片加载时序图

GIF加载时序图

glide 缓存处理

Engine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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) {


final String id = fetcher.getId();// 1. 图片url
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());

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

}
1
2
3
4
5
6
7
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {


EngineResource<?> cached = getEngineResourceFromCache(key);

return cached;
}
1
2
3
private EngineResource<?> getEngineResourceFromCache(Key key) {

}

cache : put : 时序图

cache : get : 时序图