Glide加载图片主流程分析
Glide源码地址:https://github.com/bumptech/glide
Glide版本:3.7.0
使用Glide的时候主要三步代码,如下所示:
Glide.with(this).load("url").into(iv_glide);
Glide.with(this)
获取一个RequestManager
对象
public static RequestManager with(Activity activity) {
// 饿汉式 单例模式创建
RequestManagerRetriever retriever = RequestManagerRetriever.get();
// 获取一个RequestManager对象
return retriever.get(activity);
}
RequestManager
RequestManager类的注释是:A class for managing and starting requests for Glide
,意思是一个为Glide管理和开启请求的类。
load方法:
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
fromString方法:
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
loadGeneric方法,就是负责创建DrawableTypeRequest对象,泛型是String类型。
最终调用的是DrawableTypeRequest类的load方法。
DrawableTypeRequest
load方法:
@Override
public DrawableRequestBuilder<ModelType> load(ModelType model) {
super.load(model);
return this;
}
调用了父类GenericRequestBuilder
的load方法:
GenericRequestBuilder
类load方法:
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}
这里其实就是将参数传递进来,整个流程相当于只是配置了一下加载的参数。
into(view)流程
点进去into方法,进入DrawableRequestBuilder
类的into方法,这里调用了父类的into方法。
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
进入父类GenericRequestBuilder
类的into方法:
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread(); // 保证在主线程执行
// 省略部分代码
......
return into(glide.buildImageViewTarget(view, transcodeClass));
}
这里首先调用了buildImageViewTarget
方法:
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
这里通过一个工厂创建Target,ImageViewTargetFactory在glide创建的时候就创建了。
private final ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
这里顺便看一下ImageViewTargetFactory
工厂:
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)");
}
}
}
这里会根据条件分别创建GlideDrawableImageViewTarget
、BitmapImageViewTarget
、DrawableImageViewTarget
回到into
方法,
public <Y extends Target<TranscodeType>> Y into(Y target) {
// 判断当前请求状态
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);
方法。
进入RequestTracker
类的runRequest
方法:
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
RequestTracker
类有个Set集合存储请求,有个List集合存储等待请求,这个后续研究。
进入request.begin();
方法,会进入Request 的接口类,找到其实现类GenericRequest
类:
@Override
public void begin() {
......
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
// 确定好图片大小之后,开始处理
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
// 加载占位图,后续分析
target.onLoadStarted(getPlaceholderDrawable());
}
......
}
这里主要做了两件事:1、确定好图片大小之后,开始处理;2、加载占位图,这个后续分析。
进入onSizeReady
方法:
@Override
public void onSizeReady(int width, int height) {
......
// 判断是否是等待图片大小处理
if (status != Status.WAITING_FOR_SIZE) {
return;
}
// 把状态改为运行
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
// 获取处理数据的DataFetcher
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
......
// 获取解码器
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
......
loadedFromMemoryCache = true;
// 开始加载数据
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
......
}
从上面注释得知,前面处理了一些逻辑之后,最终调用的是engine.load
方法,
进入engine.load
方法:
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();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
// 创建唯一标识,缓存用
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
// 从内存缓存中获取,不正在使用的内存缓存
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
......
// 从活动内存缓存中获取,即正在使用的图片
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
......
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 runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
// 开始执行线程
engineJob.start(runnable);
......
return new LoadStatus(cb, engineJob);
}
这段代码主要执行了:
- 判断是否主线程
- 创建唯一标识,缓存用
- 从内存缓存中获取,不正在使用的内存缓存
- 从活动内存缓存中获取,即正在使用的图片
- 创建工作引擎
- 创建解码器
- 创建执行线程
- 开始执行线程
其中缓存加载机制,这里暂不分析,主要分析开始执行线程步骤。
进入EngineJob
类的engineJob.start(runnable);
方法
public void start(EngineRunnable engineRunnable) {
this.engineRunnable = engineRunnable;
future = diskCacheService.submit(engineRunnable);
}
从代码得知,执行了diskCacheService.submit(engineRunnable);
方法:
diskCacheService其实就是一个线程池,这里使用线程池执行异步请求,既然使用线程池了,那么可以回头看一下线程做了什么操作。
回到创建执行线程EngineRunnable
类,该类继承Runnable接口,找到run方法:
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
...
// 主要是这行,解码
resource = decode();
...
// 取消就释放资源
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
// 失败回调
onLoadFailed(exception);
} else {
// 完成回调
onLoadComplete(resource);
}
}
进入decode()
方法,如下:
private Resource<?> decode() throws Exception {
// 是否从缓存中解码
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
// 从资源中解码
return decodeFromSource();
}
}
这里不做缓存分析,进入decodeFromSource()
方法:
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
进入decodeJob.decodeFromSource()
方法:
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
这里调用了decodeSource()
方法,进入该方法:
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
......
// 从网络加载数据
final A data = fetcher.loadData(priority);
......
// 解码数据
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
这里主要做了两件事:
- 从网络加载数据
- 解码数据
先分析fetcher.loadData(priority)
是如何加载数据的?
进入fetcher.loadData(priority)
方法,进入到DataFetcher
接口,找到该接口的实现类HttpUrlFetcher
类,并找到该类的loadData
方法:
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
进入loadDataWithRedirects
方法:
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
throws IOException {
......
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(2500);
urlConnection.setReadTimeout(2500);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
urlConnection.connect();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
.......
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else {
......
}
}
这里就开启了大家所熟悉的HttpURLConnection
网络请求。
再分析decoded = decodeFromSourceData(data);
private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;
// 是否开启了磁盘缓存,开启之后就把数据解码并缓存到磁盘
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
......
// 没有开启磁盘缓存就直接返回解码数据
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
......
}
return decoded;
}
如何解码的,这里不做分析。从网络下载完数据,就返回到EngineRunnable
的run方法,run方法最后调用了onLoadComplete(resource);
将数据通过回调返回出去。
进入onLoadComplete
方法:
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}
这里调用了EngineRunnableManager
的onResourceReady
方法。
Glide如何将数据设置到ImageView上的?
下面分析Glide是如何将数据设置到ImageView上的。
进入onResourceReady
方法,到ResourceCallback接口,从前面的一段代码可以得知:
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
需要找到EngineRunnableManager
的实现类EngineJob
的onResourceReady
方法:
@Override
public void onResourceReady(final Resource<?> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
这里使用handler将子线程消息发送到主线程,所以下一步在本类找到handleMessage
方法:
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;
}
}
找到匹配MSG_COMPLETE
的执行方法job.handleResultOnMainThread();
:
private void handleResultOnMainThread() {
......
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
......
// 计数
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
// 这里使用了发布订阅模式,将消息发送到订阅者
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
// 释放资源
engineResource.release();
}
这里需要注意的是下面这个for循环,这里其实使用了发布订阅模式,遍历所有订阅了ResourceCallback的订阅者,然后将engineResource发布出去。
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
这里分析一下cbs,这里要从前面说起:
进入GenericRequest
的onSizeReady
方法:
@Override
public void onSizeReady(int width, int height) {
......
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this);
......
}
这里engine.load
方法的最后一个参数传的是this。点进去load方法如下:
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)
其实最后一个参数就是ResourceCallback
接口,而该方法中调用了engineJob.addCallback(cb);
将ResourceCallback添加到EngineJob
类的cbs集合中:
private final List<ResourceCallback> cbs = new ArrayList<ResourceCallback>();
public void addCallback(ResourceCallback cb) {
Util.assertMainThread();
if (hasResource) {
cb.onResourceReady(engineResource);
} else if (hasException) {
cb.onException(exception);
} else {
cbs.add(cb);
}
}
添加到集合中,最后再从集合中将每一个ResourceCallback进行通知,其实就是使用了发布—订阅模式。
回到GenericRequest
的onSizeReady
方法中,engine.load
传this
参数,表示当前实现了ResourceCallback
接口,查看GenericRequest
类可以发现确实如此:
public final class GenericRequest<A, T, Z, R> implements Request, SizeReadyCallback,
ResourceCallback {}
那么必然实现了ResourceCallback
接口中的方法,其实一个是成功回调,一个是失败回调:
public interface ResourceCallback {
void onResourceReady(Resource<?> resource);
void onException(Exception e);
}
然后在找到GenericRequest
中对ResourceCallback
接口的实现方法onResourceReady
:
@Override
public void onResourceReady(Resource<?> resource) {
......
Object received = resource.get();
......
onResourceReady(resource, (R) received);
}
这里又调用了两个入参的onResourceReady
方法:
private void onResourceReady(Resource<?> resource, R result) {
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
......
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
// 重点
target.onResourceReady(result, animation);
}
......
notifyLoadSuccess();
}
最终调用了target.onResourceReady(result, animation);
方法,将结果传给了target,那么target又是如何将数据设置给ImageView的呢?下面就分析一下。
回忆一下最开始的地方,在执行DrawableRequestBuilder
类的into方法的时候,调用了父类GenericRequestBuilder
的into方法,
public Target<TranscodeType> into(ImageView view) {
......
return into(glide.buildImageViewTarget(view, transcodeClass));
}
这里调用了into(glide.buildImageViewTarget(view, transcodeClass));
方法,这样需要关注一下glide.buildImageViewTarget(view, transcodeClass)
方法,意思是创建一个ImageViewTarget
。
进入buildImageViewTarget
方法:
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
进入imageViewTargetFactory.buildTarget(imageView, transcodedClass);
方法:
@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)");
}
}
这里有GlideDrawableImageViewTarget
、BitmapImageViewTarget
、DrawableImageViewTarget
三种类型的target,查看他们的源码可以看到都继承自ImageViewTarget
方法,并且都实现了setResource
方法
public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {
......
@Override
protected void setResource(Drawable resource) {
// 设置Drawable图片
view.setImageDrawable(resource);
}
}
public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> {
......
@Override
protected void setResource(Bitmap resource) {
// 设置Bitmap图片
view.setImageBitmap(resource);
}
}
public class GlideDrawableImageViewTarget extends ImageViewTarget<GlideDrawable> {
......
@Override
protected void setResource(GlideDrawable resource) {
// 设置Drawable图片
view.setImageDrawable(resource);
}
再找到他们的父类ImageViewTarget
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z> implements GlideAnimation.ViewAdapter {
......
@Override
public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
setResource(resource);
}
}
protected abstract void setResource(Z resource);
从ImageViewTarget
类可以发现实现了onResourceReady
方法,这里调用了setResource
抽象方法,在子类中会执行给ImageView加载图片的操作。
这里追本溯源,再找到ImageViewTarget
类的父类ViewTarget
,然后是BaseTarget
,最后是Target
接口。
最后来一张加载流程时序图: