基于com.github.bumptech.glide:glide:3.7.0
这是一篇快速过源码,而非品味细枝末节的分析,否则简书的2W byte的限制,可能要分好几期才能彻彻底底的讲完。
引入
在github上(https://github.com/bumptech/glide) ,Glide的官方Sample使用的是如下的一段的代码:
Glide
.with(myFragment)
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.crossFade()
.into(myImageView);
我们就以此来作为出发点。
可以看到整个请求使用的当前流行的流式代码,我们来逐个击破。
初步调查
- Glide
注释写的很明白,是一个提供请求接口的单例,和我们熟悉的门面模式很相似,它是一个请求的入口,你可以看到,类中的很多方法都是静态的,直接通过Glide来调起的。
那么我们直奔主题,看一看with方法,你会发现有很多重载,但是最后都是统一进入了fragmentGet或者是supportFragmentGet来获得一个RequestManager对象
RequestManager supportFragmentGet(Context context, FragmentManager fm) { //根据传入的Fragment来获取RequestManager SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm); RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; }
- Glide
会根据你具体传入的类型的不同,最终选择3.0+的Fragment还是AppCompat的Fragment。由于两种不同的Fragment的FragmentManager是不同的,此处有两种方法`getSupportRequestManagerFragment` 和`getRequestManagerFragment`,实际上原理是一样的。
SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
//当前fragment栈中是否有我们需要的fragment在
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(
FRAGMENT_TAG);
if (current == null) {
//如果不存在,去我们的缓存Map中取
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
//如果依然没有去生成这个Fragment
current = new SupportRequestManagerFragment();
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//抹去缓存map中该key值
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
Glide利用生成额外的无界面Fragment到Framgent栈中,用来同步context的生命周期。
* RequestManager
A class for managing and starting requests for Glide
用来管理和发起请求的类。
我们顺着load方法去看,又是个重载方法,根据传入的type的类型不同,返回不同类型的`GenericRequestBuilder`
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) {
…
}
DrawableRequestBuilder.java
@Override
public DrawableRequestBuilder<ModelType> load(ModelType model) {
super.load(model);
return this;
}
GenericRequestBuilder.java
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}
最后得到的是一个`DrawableRequestBuilder<ModelType>`对象,强转成`DrawableTypeRequest<ModelType>`。
* GenericRequestBuilder的不断构造
我们通过centerCrop方法去进中央裁剪,DrawableTypeRequest的centerCrop方法在父类DrawableRequestBuilder中
@Override
public DrawableRequestBuilder<ModelType> transform(Transformation<GifBitmapWrapper>… transformation) {
super.transform(transformation);
return this;
}
GenericRequestBuilder.java
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transform(
Transformation<ResourceType>… transformations) {
isTransformationSet = true;
if (transformations.length == 1) {
transformation = transformations[0];
} else {
transformation = new MultiTransformation<ResourceType>(transformations);
}
return this;
}
修改内部参数,并且以构造者模式返回自己本身以供继续修改使用。
之后的`placeholder`方法也是继续修改GenericRequestBuilder的参数,为请求增加占屏图片。
`crossFade`方法则是增加了animationFactory参数
GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> animate(
GlideAnimationFactory<TranscodeType> animationFactory) {
if (animationFactory == null) {
throw new NullPointerException(“Animation factory must not be null!”);
}
this.animationFactory = animationFactory;
return this;
}
期间方法不单单有transform,包括priority修改优先级,encoder修改编码方式,diskCacheStrategy修改硬盘缓存策略等等。
* ###最后的into方法
之前都是在买材料囤货,就是这个方法开始做请求。
DrawableRequestBuilder.java
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
GenericRequestBuilder.java
public Target<TranscodeType> into(ImageView view) {
//判断是否在主线程
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException(“You must pass in a non null View”);
}
//如果imageView本身有填充方式,请求那么做相应的处理
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));
}
然后通过传入view和转码类型(transcodeClass)来创建相应的Target。
ImageViewTargetFactory.java
```
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)");
}
}
OK,我们继续往下看,看下重载的方法into(Target)
public <Y extends Target<TranscodeType>> Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
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;
}
Glide会先去看这个target之前有没有过请求,如果这个target之前有过请求要把这个请求clear掉,并且recycle。也就是说,如果一个imageView上我们要多次做加载请求,那么最后以最后一次请求为准。这个在listView或者是RecyclerView中使用就相当频繁了。
看到target.getRequest()方法,根据之前我们说的可能会生成的三种不同的target,我们这里去看BitmapImageViewTarget
,最终的getRequest方法是在父类ViewTarget
中
public Request getRequest() {
Object tag = getTag();
Request request = null;
if (tag != null) {
if (tag instanceof Request) {
request = (Request) tag;
} else {
throw new IllegalArgumentException("You must not call setTag() on a view Glide is targeting");
}
}
return request;
}
private Object getTag() {
if (tagId == null) {
return view.getTag();
} else {
return view.getTag(tagId);
}
}
所以很明显了,Glide玩的套路是把request对象通过setTag的方式和View绑定的
继续看request是如何build出来的
```
private Request buildRequest(Target<TranscodeType> target) {
//如果实现没有优先级的规定,设置为优先级普通
if (priority == null) {
priority = Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}
private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
if (thumbnailRequestBuilder != null) {
//缩略图的相应的request创建
…
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
…
} else {
// Base case: no thumbnail.
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
}
}
private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderId,
errorPlaceholder,
errorId,
fallbackDrawable,
fallbackResource,
requestListener,
requestCoordinator,
glide.getEngine(),
transformation,
transcodeClass,
isCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
}
我们看到真正的生成请求方法`obtainRequest`,传入了大量的参数,我们不一一深究是什么东西,我们先接着看生成了请求之后的下一个方法,` requestTracker.runRequest(request);`
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
requestTracker内部维护了一个请求列表,那我们直接进入到request实现类,去看看begin方法到底做了什么。
@Override
public void begin() {
startTime = LogTime.getLogTime();
//就是之前提到的load传入类型,如果都没有加载类型就抛异常
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
//如果已经拿到了尺寸就进入加载流程,否则继续View的尺寸
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
//如果正在请求,那么就为view填充占位图
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
终于我们找到最终的加载方法在这个onSizeReady回调中
@Override
public void onSizeReady(int width, int height) {
//参数准备
...
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));
}
}
最终GenericRequest把加载任务都扔到了engin中去了
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();
//创建每一次任务的加载的唯一标识key
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
// 通过key查找内存缓存中是否存在引擎资源,如果有就直接可以拿来用,触发onResourceReady回调(一级缓存)
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;
}
// 通过key查找是否存在弱引用可以利用(二级缓存)
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;
}
//再没有就去本地的map列表中通过key查找是否存在EngineJob,这与上面的EngineResource不同(三级缓存)
EngineJob current = jobs.get(key);
if (current != null) {
//加入内部的callback队列,最终也会执行如上的onResourceReady回调
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//如果都没有,去创建一个EngineJob,去做加载请求
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);
//执行job
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
这里涉及到多级缓存,以及最终的请求任务加载,我们打开`EngineRunnable`,来看下最终是怎么请求的。
* ###藏得最深的DecodeJob
EngineRunnable.java
```@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
//缓存中已经有了我们需要的data
return decodeFromCache();
} else {
//缓存中还没有我们需要的data,我们需要先去获得data,再将data转为我们需要的model类型
return decodeFromSource();
}
}
两个方法,一个是缓存中处理过此类请求,直接从缓存中请求,我们直接看第二个decodeFromSource
decodeFromSource()->DecodeJob.decodeFromSource()->DecodeJob.decodeSource()->DecodeJob.decodeFromSourceData(),
最终,我们找到这一句
//将data进行decode,变成我们需要的decoded类型
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
包括,走另一条decodeFromCache,也会走到这一句。经过多次的倒推与查找,我们发现在Glide的构造函数中,有着这么一坨代码
dataLoadProviderRegistry = new DataLoadProviderRegistry();
StreamBitmapDataLoadProvider streamBitmapLoadProvider =
new StreamBitmapDataLoadProvider(bitmapPool, decodeFormat);
dataLoadProviderRegistry.register(InputStream.class, Bitmap.class, streamBitmapLoadProvider);
FileDescriptorBitmapDataLoadProvider fileDescriptorLoadProvider =
new FileDescriptorBitmapDataLoadProvider(bitmapPool, decodeFormat);
dataLoadProviderRegistry.register(ParcelFileDescriptor.class, Bitmap.class, fileDescriptorLoadProvider);
ImageVideoDataLoadProvider imageVideoDataLoadProvider =
new ImageVideoDataLoadProvider(streamBitmapLoadProvider, fileDescriptorLoadProvider);
dataLoadProviderRegistry.register(ImageVideoWrapper.class, Bitmap.class, imageVideoDataLoadProvider);
GifDrawableLoadProvider gifDrawableLoadProvider =
new GifDrawableLoadProvider(context, bitmapPool);
dataLoadProviderRegistry.register(InputStream.class, GifDrawable.class, gifDrawableLoadProvider);
dataLoadProviderRegistry.register(ImageVideoWrapper.class, GifBitmapWrapper.class,
new ImageVideoGifDrawableLoadProvider(imageVideoDataLoadProvider, gifDrawableLoadProvider, bitmapPool));
dataLoadProviderRegistry.register(InputStream.class, File.class, new StreamFileDataLoadProvider());
register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
register(File.class, InputStream.class, new StreamFileLoader.Factory());
register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(int.class, InputStream.class, new StreamResourceLoader.Factory());
register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
register(String.class, InputStream.class, new StreamStringLoader.Factory());
register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
其实Glide早就已经把基本所有的加载请求情况都已经考虑在内了。
dataLoadProviderRegistry注册的是将data转换成resource的情况
register方法注册是将model转换成data。
DecodeJob中的两个重要的类就是和上面的东西相关的
private final DataFetcher<A> fetcher;
private final DataLoadProvider<A, T> loadProvider;
fetcher负责把model转换成data
loadProvider再负责把data转换成我们需要的资源类型resource。
在上面的decode流程中的decodeSource()方法我们能看到fetcher的调用,也只有在这个方法中我们可以看到fetcher的调用,因为只有缓存中没有现存的data,我们才会去做一次model转换成data。
追根溯源,你会发现,最终这个fetcher就是上面register方法中的XXXXXLoader.getResourceFetcher返回的DataFetcher对象,我们就取一个Http的请求看一下:
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());]
此处说明一下,之所以选择GlideUrl转换成输入流是因为,所有的http和https的网络url最后都会被转换成GlideUrl,具体原因见UriLoader.java
@Override
public final DataFetcher<T> getResourceFetcher(Uri model, int width, int height) {
...
if (isLocalUri(scheme)) {
...
} else if (urlLoader != null && ("http".equals(scheme) || "https".equals(scheme))) {
result = urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height);
}
return result;
}
HttpUrlFetcher.java
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new IOException("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 IOException("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(2500);
urlConnection.setReadTimeout(2500);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Connect explicitly to avoid errors in decoders if connection fails.
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");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else {
if (statusCode == -1) {
throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
}
throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
}
}
网络请求就是在这里去执行的,使用的HttpUrlConnection。
总结
一次请求流程大致如下
- 首先通过Glide.with方法生成RequestManager对象来管理请求
- 调用RequestManager.load方法来讲我们的modelType传入,并得到相应的RequestBuilder对象。
- 通过构造者模式不断去给RequestBuilder增加条件,比如裁剪,优先级,占位图等等
- 通过into方法传入目的target,并开启请求
- 查看目标View的tag中获取看看是否有request,如果有则清除。然后用新建的request来覆盖。
- 执行request,三级缓存策略,先看缓存中是否存在EngineResource,再看是否有EngineResource的若引用,最后看Map中是否存在EngineJob。如果有则直接返回结果并进行相应加载
- new并执行EngineRunnable这个DecoderJob的封装
- 在DecoderJob内部查看是否存在相应的InputStream或者是ParcelFileDescriptor,如果已经存在,则直接将其通过
loadProvider
decode成相应的Bitmap,gif等。否则就通过fetcher
先将我们通过load传入的路径进行解析成InputStream、ParcelFileDescriptor,再decode。