Glide最简单的使用应该就是下边的代码:
Glide.with(Context).load("url").into(ImageView);
开发者使用起来非常简单,但里面大有文章。 之前看了Retrofit、Volley、OkHttp库源码都感觉自己看下来没有太大问题。难度不算大。但Glide的源码如果你有能力不需要其他资源能看下来的确能力很了得。因为Glide的源码复杂程度绝对在上面说的几个框架之上。 在根据上边范例代码看源码过程中我画了个UML图。基本上都不够画,只能在into部分以最简单的方式呈现出来。上图:
大致说一下这个图的三大部分就是:
- .with(Context)
- .load("url")
- .into(ImageView)
.with(Context)
哪两步:就是红色长方形框部分get(FragmentActivity)或get(Activity); 而红色圈圈其实最终走getApplicationManager(context)
####getApplicationManager(context) 该方法属于工作线程调用with或with里面的参数是Application的Context
- ApplicationLifecycle 其实看名字大概就知道它是监听Application的
- EmptyRequestManagerTreeNode这个因为是Application所以其实它是什么也没有做。
####get((Activity) context);
只看截图应该知道大概干了什么事情那吧:
- 创建RequestManagerFragment
- 在该fragment获取lifecycle,和RequestManagerTreeNode
其实get(FragmentActivity activity)也是大致做这样的事情:
为什么要分开做主要是因为在android中有两种fragment,一种是v4和另外一种是非v4包的。
####Lifecycle和RequestManagerTreeNode分别干什么
- ActivityFragmentLifecycle 实现 Lifecycle接口 主要就是管理所有实现Lifecycle的接口。
- RequestManagerTreeNode RequestManagerTreeNode有两个实现它接口的类:SupportFragmentRequestManagerTreeNode和FragmentRequestManagerTreeNode 其实作用都是一样就是收集它们层级的RequestManager。因为我们在每个Activity或Fragment调用Glide.with(this);就会创建一个RequestManager。而RequestManagerTreeNode的作用就是获取RequestManager的集合Set。
####RequestManager的创建过程:
- RequestTracker其实就是有点和ActivityFragmentLifecycle的功能类似就是将Request收集起来,进行统一管理。
- ConnectivityMonitorFactory 的主要作用就是监听网络情况。而监听网络主要通过广播。 ConnectivityMonitorFactory会根据Manifest文件中是否有添加userPermission对网络进行监听;
整个with阶段Glide主要就是做了这些东西:
- 创建一个空的Fragment对生命周期进行监听,在onStart执行request.onStart();
- 通过TreeNode对每个Fragment都进行管理;
- 通过RequestTracker对每个Request进行管理;
- 网络监听,通过广播对网络进行监听;
- Glide对象的创建,会在后面交待清楚; 缓存方面的操作在本文中不打算分析,以后有机会在学习。 下面看看load(url)部分
###.load(url)
RequestManager的load()方法:
我们就看load(string)
- fromString()
####这里留意一下泛型,后面会出现四个泛型的对象,到时更晕
####现在返回来看看Glide对象的构建:
- 现在看回去刚才的loadGeneric方法:
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
一路跟下去最终就是调用刚才说的GenericLoaderFactory的buildModelLoader。这里我们可以根据泛型知道对应的ModelLoader:String InputStream
相同的方法就会发现
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(modelClass, context);
fileDescriptorModelLoader 是 FileDescriptorUriLoader
最后是new DrawableTypeRequest对象 optionsApplier.apply其实也是返回DrawableTypeRequest,所以看DrawableTypeRequest对象构建:
这里传了一大堆参数其实,很多参数是我们之前讲过的,这里就不介绍了。这里主要看buildProvider方法: 它是返回一个FixedLoadProvider, 这里buildProvider主要看:下面部分
这里记录一下DrawableTypeRequest各个Loader、Provider待会会用得到:
- LoadProvider:FixedLoadProvider
- streamModelLoader :HttpUrlGlideUrlLoader
- fileDescriptorModelLoader:FileDescriptorUriLoader
- ResourceTranscoder : GlideBitmapDrawableTranscoder
- DataLoadProvider : ImageVideoGifDrawableLoadProvider
- modelLoader : ImageVideoModelLoader
基本上load()方法就是根据ModelLoader和传入在Glide注册的Loader,Provider选取各种需要的Loader和Provider。
###into(ImageView)
- DrawableTypeRequest.into(ImageView) DrawableTypeRequest并没有into方法,它的父类DrawableRequestBuilder有:
into(glide.buildImageViewTarget(view, transcodeClass));
返回一句Target,Target是一个接口; 先看看glide.buildImageViewTarget(view,transcodeClass);这句代码; 这里的transcodeClass是 GlideDrawable.class
glide.buildImageViewTarget(view, transcodeClass)
代码一直跟落去会看到上面截图; 返回Target; 这里其实你一直跟落去GlideDrawableImageViewTarget会发现是通过view获取宽高等信息。关键是看泛型是什么东西: 其实不难发现泛型Z就是:GlideDrawable.class 这里不多作解释; 即返回的Target为:GlideDrawableImageViewTarget 那么into最终其实就是into(GlideDrawableImageViewTarget);
Request request = buildRequest(target);
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);
}
####其实最终就是构建一个Request
Request是什么其实就是加载图片的请求;前面的RequestManager就是负责管理Request的; 太多参数只能截部分图; 你会看到四个泛型分别是<A,T,Z,R>
从我们的例子里,
- A String
- T ImageVideoWrapper
- Z GifBitmapWrapper
- R GlideDrawable
继续往下看
Request创建完成后就用target set Request; 然后设置监听器 最后用RequestTracker运行Request;
看Request.begin() 上面已经说了真正实现Request的是GenericRequest 所以看GenericRequest的begin()
onSizeReady(overrideWidth, overrideHeight);
####所有谜底都在这里
- 包括线程;
- 网络执行
- 回调
前面的取宽高其实是缩略图的宽高; 主要看下面的代码:
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader(); final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height);
这里的modelLoader 即 ImageVideoModelLoader dataFetcher 即 ImageVideoFetcher
这个截图是上面load(url)阶段时候记录的。现在用得上啦吧。说Glide源码复杂就在这里,太多这些loader,provider的东西了。
继续....
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
transcoder 这里的是 GlideBitmapDrawableTranscoder;上面也提到过;
####最后阶段了
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder, priority, isMemoryCacheable, diskCacheStrategy, this);
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();
//这里的id其实就是url地址
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);
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);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//工作线程
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);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
大Boss来了,这里就是核心部分,什么网络加载图片,编码解码的工作都在这里完成。
由于这博文不说缓存部分所以很多代码可以忽略; 主要看:
其实看名字都大概知道干什么的; 创建EnginJob主要处理Runnable和回调 DecodeJob就是解码 最终new LoadStatus就是更新Request的状态
这里主要看看Runnable的代码:
这里的fetcher即DataFetcher,实现DataFetcher是 ImageVideoModelLoader,而ImageVideoModelLoader的loadData();
这里的streamFetcher 就是HttpUrlFetcher
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());
}
}
加载图片资源由HttpUrlFetcher的loadData完成。 最终返回 ImageVideoFetcher 往下看:
真正解码是通过该类完成。
@Override
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
final ByteArrayPool byteArrayPool = ByteArrayPool.get();
final byte[] bytesForOptions = byteArrayPool.getBytes();
final byte[] bytesForStream = byteArrayPool.getBytes();
final BitmapFactory.Options options = getDefaultOptions();
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(
is, bytesForStream);
// Use to retrieve exceptions thrown while reading.
// TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a way to determine
// if a Bitmap is partially decoded, consider removing.
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
// Use to read data.
// Ensures that we can always reset after reading an image header so that we can still attempt to decode the
// full image even when the header decode fails and/or overflows our read buffer. See #283.
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
try {
exceptionStream.mark(MARK_POSITION);
int orientation = 0;
try {
orientation = new ImageHeaderParser(exceptionStream).getOrientation();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot determine the image orientation from header", e);
}
} finally {
try {
exceptionStream.reset();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot reset the input stream", e);
}
}
}
options.inTempStorage = bytesForOptions;
final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
final int inWidth = inDimens[0];
final int inHeight = inDimens[1];
final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
final Bitmap downsampled =
downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize,
decodeFormat);
// BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
// and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
// we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
final Exception streamException = exceptionStream.getException();
if (streamException != null) {
throw new RuntimeException(streamException);
}
Bitmap rotated = null;
if (downsampled != null) {
rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
downsampled.recycle();
}
}
return rotated;
} finally {
byteArrayPool.releaseBytes(bytesForOptions);
byteArrayPool.releaseBytes(bytesForStream);
exceptionStream.release();
releaseOptions(options);
}
}
是Downsampler的decode方法 最终如果是非gif格式的图片返回:GifBitmapWrapper
###最终获取到非gif格式的bitmap放入Imageview上。 由于看Glide源码之前我是看了Volley、Retrofit、OkHttp这三个的源码感觉还好,所以就直接在没有看其他资源的基础上看Glide的源码。但看到with阶段就有困难,卡在Glide是通过什么监听Activity的生命周期,然后看了看郭霖的介绍Glide的第二篇开始部分 Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程知道是通过构建空的fragment实现的。因为是第一次接触通过这样的方法实现监听Activity生命周期,所以我觉得这个是Glide的一大优点。值得我们学习。 看到load(url)阶段卡住,因为当时没有发现Glide对象的创建注册了很多provider、loader,decoder之类。主要是看源码不够细心。如是看了此篇文章Glide加载图片流程(Part One),这里其实主要看表格部分,它给了我启发。之后,我细致地看Glide对象构建就知道是通过一个接口加对象管理一大堆provider、loader,decoder。
最后想说看源码一定要:
- 细心
- 像郭霖大神所说要有目的地看源码,不然会走进了森林,走不出来。
- 耐心: 人家郭大神看了一个月Glide的源码,所以你懂得~~~哈哈
题外话一下AOSP带给我们很多值得学习的地方如:
- Volley将请求分为缓存队列和网络队列管理;
- Retrofit通过注解、集合对Factory的整理、建造者模式、动态代理实现高度解耦
- OkHttp库的递归迭代实现各个拦截的功能独立,连续调用;静态代码块实现对旧版本代码的调用