今天要和大家一起分析的是图片加载开源库Glide,目前图片加载比较流行的开源库有Picasso,Glide,Fresco等,我用过的只有Picasso和Glide,Picasso给我的唯一感觉就是代码精简,功能强大。而Glide 相对而言代码要比Picasso复杂得多。之前项目中一直用Picasso但是后来项目中需要支持Gif才切换到Glide.Glide 特点也很明显,就是它能够响应生命周期事件以及网络状态监听事件,会随着这两类事件启动,暂停,恢复网络请求。两者的相同点就是接口相近,所以从Picasso切换到Glide不会有任何的困难。

好了我们先来分析下源码:

首先是Glide对象的创建:
这里的设计思想比较值得借鉴: 首先使用单例+建造者模式来创建出Glide,而将GlideMode相当于一个插件从AndroidMenifest解析出来,每个插件可以往Glide里面添加配置以及对应的组件实例.
这里 applyOption 以及 registerComponents 分别将 GlideBuilder 以及glide.registry 丢进去,在两个方法中将当前插件的配置信息以及插件本身注册到Glide中。

public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
//使用ManifestParser来解析AndroidMenifest中的GlideModule配置
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
//创建一个GlideBuilder
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
//将从AndroidMannifest解析出来的GlideMode组件的配置添加到GlideBuilder
module.applyOptions(applicationContext, builder);
}
//创建出对应的Glide
glide = builder.createGlide();
for (GlideModule module : modules) {
//将从AndroidMannifest解析出来的GlideMode组件添加到glide.registry
module.registerComponents(applicationContext, glide.registry);
}
}
}
}
return glide;
}

实际的Glide是通过GlideBuilder的createGlide中创建的,这个套路在Picasso中也应用过,在这里主要判断加载图片所需的部件是否都进行了设置如果没有设置那么就赋给它默认的对象。
Glide中的主要可设置的组件有如下:

1. 线程池
2. 设备缓存计算器
3. Bitmap 对象池
4. 字节数组对象池
5. 内存缓存
6. 磁盘缓存
7. Glide图片加载引擎
8. 解码格式

这些会在涉及到的环节进行展开介绍。需要注意的是在解码格式的设置上,如果图片不支持透明度那么就使用565的格式,如果有透明度那么就使用8888的格式。这两种在保存的图片大小上会有影响。很显然8888的格式保存下来文件会相对大。但是效果会好很多。

Glide createGlide() {

if (sourceService == null) {
//判断线程池是否已经设置,如果没有设置,那么会使用默认的先进先出优先级队列线程池,最大的线程数为当前唤醒的CPU核数
//它主要用于当从缓存中没有找到对应的对象时执行Glide的加载,解码和转换任务
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
sourceService = new FifoPriorityThreadPoolExecutor("source", cores);
}

if (diskCacheService == null) {
//它主要用于当从缓存中有对应的对象时,用来执行Glide的加载,解码和转换任务
//如果缓存线程池没有特殊指定那么将会使用最大线程数为1的先入先出优先级队列线程池
diskCacheService = new FifoPriorityThreadPoolExecutor("disk-cache", 1);
}

if (memorySizeCalculator == null) {
//某个设备缓存计算器,计算结果取决于一些常量和当前设备的信息(宽,高,像素密度)
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}

if (bitmapPool == null) {
//Bitmap对象池初始化
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
//通过memorySizeCalculator计算出缓存大小
int size = memorySizeCalculator.getBitmapPoolSize();
if (DecodeFormat.REQUIRE_ARGB_8888) {
bitmapPool = new LruBitmapPool(size, Collections.singleton(Bitmap.Config.ARGB_8888));
} else {
bitmapPool = new LruBitmapPool(size);
}
} else {
bitmapPool = new BitmapPoolAdapter();
}
}

if (byteArrayPool == null) {
//基于LRU算法的数组缓存池
byteArrayPool = new LruByteArrayPool();
}

if (memoryCache == null) {
//内存缓存
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}

if (diskCacheFactory == null) {
//磁盘缓存
diskCacheFactory = new InternalCacheDiskCacheFactory(context, Glide.DEFAULT_DISK_CACHE_SIZE);
}

if (engine == null) {
//负责开启加载任务以及管理活跃的或者缓存的图片资源
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}

//解码格式
if (decodeFormat == null) {
//图片的加载格式(ARGB_8888或RGB_565),默认ARGB_8888,判断规则如下
//如果支持透明或者使用了透明则使用ARGB_8888
//如果不支持透明则使用ARGB_565
decodeFormat = DecodeFormat.DEFAULT;
}
return new Glide(engine, memoryCache, bitmapPool, byteArrayPool, context, decodeFormat);
}

紧接着就是调用Glide 构造方法来创建对象。在Glide 构造方法中最主要的任务就是注册一系列内置的图片资源编解码器,创建ImageViewTargetFactory,新建RequestOptions,并为其设置图片编码格式。最后将全局中比较常用的Glide,Register,RequestOption,Engine存放到GlideContext 中供后续使用,这个也是比较常用的方式。注册器会在后面进行进一步介绍。这里先主要看下整个流程。

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, ByteArrayPool byteArrayPool, Context context, DecodeFormat decodeFormat) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.byteArrayPool = byteArrayPool;
this.memoryCache = memoryCache;
//BitmapPool的预填充器
bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
Resources resources = context.getResources();
//从给定的inputstream中解码图片
Downsampler downsampler = new Downsampler(resources.getDisplayMetrics(), bitmapPool, byteArrayPool);
//Gif图片资源的解码器
ByteBufferGifDecoder byteBufferGifDecoder = new ByteBufferGifDecoder(context, bitmapPool, byteArrayPool);
//用于管理组件注册器,我们还可以自己自定义组件通过GlideModule registerComponents来进行注册
registry = new Registry(context)
//添加到encoderRegistry public <Data> Registry register(Class<Data> dataClass, Encoder<Data> encoder)
.register(ByteBuffer.class, new ByteBufferEncoder())//将ByteBuffers 数据写入到文件中
.register(InputStream.class, new StreamEncoder(byteArrayPool))//将inputStream数据写入到文件中
//..................
/* Transcoders */
.register(Bitmap.class, BitmapDrawable.class, new BitmapDrawableTranscoder(resources, bitmapPool))
.register(Bitmap.class, byte[].class, new BitmapBytesTranscoder())
.register(GifDrawable.class, byte[].class, new GifDrawableBytesTranscoder());
//用于负责生成特定类型的Target
ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
//设置解码格式
RequestOptions options = new RequestOptions().format(decodeFormat);
//通过Context形式暴露出来
glideContext = new GlideContext(context, registry, imageViewTargetFactory, options, engine, this);

}

到目前为止,Glide对象创建结束,我们回顾下整个过程:

  1. 首先是从AndroidManifest查找是否有用户自定义的GlideModule,如果有的话将其解析出来,每个GlideModule一般有两个方法,applyOptions和registerComponents ,applyOptions负责将自定义GlideModule中的配置添加到GlideBuilder中,而registerComponents 负责将当前GlideModule添加到Register中。
  2. 然后在GlideBuilder 的createGlide方法中为没有设置的Glide重要组件设置默认状态。
  3. 最后调用Glide构造方法创建对象。在构造方法中主要是完成各种资源编解码器的注册,以及Target的构建工厂对象的创建。以及设置解码格式和Glide图片加载引擎。
  4. 通过 GlideContext将后续处理常用的组件暴露出来。

Glide创建出来后就进入了with阶段:
之前提到的Glide 图片加载过程中会响应Activity/Fragment生命周期 以及网络状态而启动,恢复,暂停图片加载请求都是在这个环节中体现的。这个环节中主要的两个对象是RequestManagerFragment 以及 ConnectivityMonitor。
好了我们接下来就来带着这两个问题进一步阅读源码:

大家可以发现Glide.java 中有很多with方法,为什么需要重载这么多个呢?它们之间的区别是什么呢?

使用context启动的请求将只会拥有应用等级的options,不会根据生命周期事件来自动开始或者停止图片的加载。通常而言,如果资源在一个子Fragment中的一个View中使用,那么load就应该使用子Fragment作为参数的with方法。如果资源应用在父Fragment中的View中,那么load就应该使用父Fragment作为参数的with方法。同样的道理如果资源在Activity中使用那么就应该使用Activity作为参数的with方法,使用Context作为参数的一般用于不在常用的fragment或者activity生命周期的资源,比如services或者notification中资源

我们以最常用的Activty的情况作为分析情景:

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

第一阶段我们先看下怎么获取到RequestManagerFragment,首先我们应该明白RequestManagerFragment和我们开发过程中常见的Fragment的区别是它是一个没有界面不可见的一个Fragment,但是它有Fragment所拥有的生命周期。我们可以借助这一点来实现前面提到的图片加载请求状态随着生命周期的改变而改变。

public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
//获取它的FragmentManager,并且将activity以及FragmentManager传入fragmentGet获取RequestManagerFragment
android.app.FragmentManager fm = activity.getFragmentManager();
//获取RequestManagerFragment
return fragmentGet(activity, fm);
}
}

那么对于Activity而言这个Fragment藏在哪里呢?它和Activty所拥有的一般Fragment一样都归到Activity FragmentManager中统一管理。我们看下这个过程:

RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
//从指定的FragmentManager获取对应的RequestManagerFragment,
//如果没有那么就直接新建一个RequestManagerFragment添加到FragmentManager
RequestManagerFragment current = getRequestManagerFragment(fm);
//从RequestManagerFragment中获取RequestManager
RequestManager requestManager = current.getRequestManager();
//如果都没获取到那么就新建一个RequestManager将其设置到RequestManagerFragment
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}

从上面代码可以看出,这个阶段首先会先从FragmentManger 中获取,如果没获取到就会新建一个并添加到FragmentManager,获取完RequestManagerFragment后就从中获取RequestManager,下面是获取RequestManagerFragment的过程代码:

RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
//使用Fragment查找TAG为com.bumptech.glide.manager的Fragment
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//尝试从pendingRequestManagerFragments获取,有可能还在缓存中
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
//新建一个RequestManagerFragment,添加到pendingRequestManagerFragments缓存
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
//将新建的RequestManagerFragment添加到FragmentManager中
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//提交成功后移除pendingRequestManagerFragments中的RequestManagerFragment
//但是为什么不直接调用pendingRequestManagerFragments.remove方法呢?
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}

我们上面介绍了RequestManagerFragment的获取方式,那么RequestManagerFragment又是怎么将请求和生命周期给对应起来的呢?在回答这个问题之前我们先来看下RequestManagerFragment的结构,RequestManagerFragment中有两个重要对象ActivityFragmentLifecycle,RequestManager
前者是生命周期的触发者,需要响应生命周期的部件都需要监听它,而RequstManger负责请求的管理,它里面有个RequestTracker 用于实行请求的管理。RequstManaer是生命周期事件的响应者,RequestManagerFragment触发对应生命周期后通过ActivityFragmentLifecycle传出来,ActivityFragmentLifecycle再通知监听它的监听者。对应的监听者在不同的生命周期作出不同的响应。

我们来看下这部分的代码
ActivityFragmentLifecycle 是在RequestManagerFragment构造方法中创建的,如下所示:

public RequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}

RequestManager是通过setRequestManager进行注入的,这个可以看之前的代码介绍。

public void setRequestManager(RequestManager requestManager) {
this.requestManager = requestManager;
}

我们再看下RequestManagerFragment的生命周期中做了什么,和之前说的一样它在对应的生命周期中触发lifecycle事件,

@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}

@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}

@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
}

ActivityFragmentLifecycle 很简单就是注册对应生命周期事件的观察者,一旦ActivtyFragmentLifeCyble被RequestManagerFragment 生命周期所触发,那么ActivtyFragmentLifeCycle也会将对应的事件通知到各个观察者。

@Override
public void addListener(LifecycleListener listener) {
//添加监听器,并将当前状态通知到其观察者
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}

@Override
public void removeListener(LifecycleListener listener) {
lifecycleListeners.remove(listener);
}

void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}

void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}

void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}

ActivtyFragmentLifeCycle 有两个重要的观察者:RequestManager 和 ConnectivityMonitor。所以这个阶段的整个过程如下图所示:

到此为止with阶段代码分析完毕,我们来简单回顾下:
这个阶段主要是完成请求随着生命周期的状态改变而改变状态的工作。主要涉及到四个重要对象一个是RequestManagerFragment 一个是 ActivityFragmentLifeCycle ,还有两个是RequestManger,以及ConnectivityMonitor。三者之间的关系以及如何实现请求随着生命周期的状态改变而改变状态这个在上面已经详细介绍过了。这个需要重点了解下。

接下来是as阶段:

load 有多种实现,现将这些实现都罗列如下:

/**
* 将资源指定加载为Bitmap,即使实际上可能是一个动画也会将资源加载为Bitmap
*/
public RequestBuilder<Bitmap> asBitmap() {
return as(Bitmap.class).transition(new BitmapTransitionOptions()).apply(DECODE_TYPE_BITMAP);
}

/**
* 尝试将资源总是以GifDrawable形式加载
* 这种情况如果数据不是GIF,则会失败。因此,这只能使用与下面的情况:
* 如果数据代表动画GIF,并且调用者想要与GifDrawable进行交互
* 通常情况只需要使用asDrawable就足够了,因为它会确定给定的数据是否代表动画GIF并返回相应的Drawable
*/
public RequestBuilder<GifDrawable> asGif() {
return as(GifDrawable.class).transition(new DrawableTransitionOptions()).apply(DECODE_TYPE_GIF);
}

/**
* 尝试使用注册的ResourceDecoder来加载指定的资源
* 默认情况下,返回的要么是个BitmapDrawable要么是个GifDrawable 但是如果其他用于解码Drawable的解码器有注册,那么它们有可能也会被返回
*/
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class).transition(new DrawableTransitionOptions());
}

上面最终都是调用如下方法:

/**
* 尝试使用任何注册的能够解析给定的资源class或者给定资源class子类的ResourceDecoder来加载资源
*/
public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
return new RequestBuilder<>(context, this, resourceClass);
}

从这里可以看出load阶段的工作就是创建一个RequestBuilder。

创建了一个RequestBuilder后就可以调用load开始加载了。我们来看下load阶段:

和as以及with阶段一样load也有各种重载,但是最终还是调用:

private RequestBuilder<TranscodeType> loadGeneric(Object model) {
//这个很简单设置model并且将isModelSet设置为true
this.model = model;
isModelSet = true;
return this;
}

这里只是简单得设置了model并将isModelSet标识为设为true后返回。很简单吧,其实Picasso这个阶段也很简单,不信可以看下我之前写的博客。

Glide对象创建了,请求状态随生命周期改变而改变实现了,RequestBuilder创建出来了,model设置了,接下来就是最重要的图片加载流程了,也就是into阶段:

into阶段实际上就是开始加载图片数据,并将图片数据设置到Target上,我们这里以ImageView为Target来看下这个流程:

public Target<TranscodeType> into(ImageView view) {
//..........
if (!requestOptions.isTransformationSet() && view.getScaleType() != null) {
if (requestOptions.isLocked()) {
requestOptions = requestOptions.clone();
}
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions.optionalCenterCrop(context);
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions.optionalFitCenter(context);
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(context.buildImageViewTarget(view, transcodeClass));
}

在这个阶段最重要的是最后一行代码into(context.buildImageViewTarget(view, transcodeClass))

public <X> Target<X> buildImageViewTarget(ImageView imageView, Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}

这里使用Glide对象构造阶段创建的ImageViewTargetFactory来创建出我们需要的Target,具体需要创建什么Target需要根据transcodeClass来决定。

public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
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)");
}
}

而transcodeClass 是在创建RequestBuilder的时候传入的,而RequestBuilder是在as方法中创建的,所以这里的resourceClass就是buildTarget所对应的transcodeClass。所以我们调用asBitmap 的时候这里传入的就是Bitmap.class,调用asGif的时候这里传入的就是GifDrawable.class,如果我们调用asDrawable的时候,这里传入的就是Drawable.class。

public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
return new RequestBuilder<>(context, this, resourceClass);
}

不管怎样buildTarget 返回的就两个对象要么是BitmapImageViewTarget要么是DrawableImageViewTarget

BitmapImageViewTarget –> ImageViewTarget
DrawableImageViewTarget –> ImageViewTarget

但是我们前面已经提到asDrawable 和asGif两个都是对应的DrawableImageViewTarget那么这两个会有什么区别呢?我们看下DrawableImageViewTarget,两种区别在于是否继承自Animatable,GifDrawable是继承自Animatable 所以在资源获取结束后会调用start方法让它动起来。

@Override
public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();

if (!(resource instanceof Animatable) && layoutParams != null && layoutParams.width > 0
&& layoutParams.height > 0) {
resource = new FixedSizeDrawable(resource, layoutParams.width, layoutParams.height);
}
super.onResourceReady(resource, transition);
if (resource instanceof Animatable) {
((Animatable) resource).start();
}
}

@Override
public void onStart() {
if (resource instanceof Animatable) {
((Animatable) resource).start();
}
}

@Override
public void onStop() {
if (resource instanceof Animatable) {
((Animatable) resource).stop();
}
}

我们再继续顺着继承树看下去,

BitmapImageViewTarget –> ImageViewTarget –> ViewTarget –> BaseTarget
DrawableImageViewTarget –> ImageViewTarget –> ViewTarget –> BaseTarget

ViewTarget 中有几个重要的回调函数onLoadStarted,onLoadFailed ,onLoadCleared ,onResourceReady
分别在启动加载,加载失败,清除加载,资源获取完毕的情况下回调,主要用于设置占位图片,错误图片以及加载完的图片

@Override
public void onLoadStarted(Drawable placeholder) {
setResource(null);
setDrawable(placeholder);
}

@Override
public void onLoadFailed(Drawable errorDrawable) {
setResource(null);
setDrawable(errorDrawable);
}

@Override
public void onLoadCleared(Drawable placeholder) {
setResource(null);
setDrawable(placeholder);
}

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

这个继承树根真深,还得继续刨:
ViewTarget有两个重要的任务就是获取这个view的Size以及设置tag:
这里有个重要的对象SizeDeterminer,获取View的尺寸就是通过这个类来完成的。它首先会先通过getWidth()和getHeight()来获取View的宽高,如果有一个为0的话,它会检查View的LayoutParams从中获取宽高大小,如果还是没有那么就会监听OnPreDrawListener回调从而等到在绘制之前进行测量的时候回调。

public void getSize(SizeReadyCallback cb) {
//调用View的getWidth 和 getHeight获取,如果获取不到尝试LayoutParams中获取
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
//如果通过上述两种方式能够获取到宽高,那么就直接调用onSizeReady将宽高传出
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
} else {
//如果通过上述两种方式都不能获取到高度那么只能通过添加OnPreDrawListener来等到该View 测量结束后返回宽高
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
final ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}
}

在测量结束的时候会调用checkCurrentDimens来检查当前的宽高

@Override
public boolean onPreDraw() {

SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
sizeDeterminer.checkCurrentDimens();
}
return true;
}

在checkCurrentDimens 会重新获取并检查View的宽高如果获取到了就通过notifyCbs来通知对应的监听者。
并将其从OnPreDrawListener监听队列中返回。

private void checkCurrentDimens() {
if (cbs.isEmpty()) {
return;
}
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
if (!isSizeValid(currentWidth) || !isSizeValid(currentHeight)) {
return;
}
notifyCbs(currentWidth, currentHeight);
ViewTreeObserver observer = view.getViewTreeObserver();
if (observer.isAlive()) {
observer.removeOnPreDrawListener(layoutListener);
}
layoutListener = null;
}

所以整个继承树的完成工作如下:
DrawableImageViewTarget –> ImageViewTarget –> ViewTarget –> BaseTarget
将加载的图片设置到Target,如果是Gif启动播放Gif –> 设置占位图,错误图缩率图等 –> 获取图片尺寸,设置Tag –> 存储Request
有了上述的了解我们再继续返回看into的流程:

public <Y extends Target<TranscodeType>> Y into(Y target) {
Util.assertMainThread();
//Target不能为空
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
//model必须设置
if (!isModelSet) {
throw new IllegalArgumentException("You must first put a model (try #load())");
}
//获取当前target中的请求
Request previous = target.getRequest();
//如果当前target存在请求,那么清除target对应的请求
if (previous != null) {
requestManager.clear(target);
}
//锁定requestOptions不让在加载过程中修改
requestOptions.lock();
//创建请求
Request request = buildRequest(target);
//将request 设置给 target
target.setRequest(request);
//requestManager开始跟踪这个请求
requestManager.track(target, request);
return target;
}

into方法中会首先检查当前target是否有请求正在进行如果有那么先清除原先的请求。调用buildRequest来创建该次请求,并调用setRequest将请求与target进行绑定(也就是为当前View打上request的标签,并将request保存下来)。然后通过ReqestManager对该请求进行track。

这里关注两点

  1. buildRequest怎么创建出请求。
  2. ReqestManager怎么使用创建出来的请求进行处理.
    private Request buildRequest(Target<TranscodeType> target) {
    return buildRequestRecursive(target, null, transitionOptions, requestOptions.getPriority(), requestOptions.getOverrideWidth(), requestOptions.getOverrideHeight());
    }

    private Request buildRequestRecursive(Target<TranscodeType> target,
    if (thumbnailBuilder != null) {
    //.....
    } else {
    // Base case: no thumbnail.
    //没有缩率图的情况
    return obtainRequest(target, requestOptions, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight);
    }
    }

    private Request obtainRequest(Target<TranscodeType> target,
    BaseRequestOptions<?> requestOptions, RequestCoordinator requestCoordinator,
    TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority,
    int overrideWidth, int overrideHeight) {
    requestOptions.lock();
    RequestContext<?, TranscodeType> requestContext = new RequestContext<>(context, model, transcodeClass, requestOptions, priority, overrideWidth, overrideHeight);
    return SingleRequest.obtain(requestContext, target, requestListener, requestCoordinator, context.getEngine(), transitionOptions.getTransitionFactory());
    }
    为了避免频繁创建和释放请求对象,这里使用了一个请求对象池,最多可以缓存150个请求。每次会从这个请求池中优先获取,只有在请求池为空的情况下才会新建一个请求,获取到一个请求对象后,就可以使用当前的请求参数初始化获取到的请求对象。这样就可以在这次请求中使用了。
    public static <R> SingleRequest<R> obtain(RequestContext<?, R> requestContext, Target<R> target,
    RequestListener<R> requestListener, RequestCoordinator requestCoordinator, Engine engine,
    TransitionFactory<? super R> animationFactory) {

    //尝试从对象池中获取
    @SuppressWarnings("unchecked")
    SingleRequest<R> request = (SingleRequest<R>) REQUEST_POOL.acquire();
    //获取失败则新建一个请求
    if (request == null) {
    request = new SingleRequest<>();
    }
    //初始化请求
    request.init(requestContext, target, requestListener, requestCoordinator, engine, animationFactory);
    return request;
    }

了解了请求的创建过程后我们继续看下如果使用获取的请求进行加载图片:
我们看下RequestManager track方法:

void track(Target<?> target, Request request) {
//监听生命周期
lifecycle.addListener(target);
//跟踪请求
requestTracker.runRequest(request);
}

track中处理很简单,就是让target监听生命周期后,调用requestTracker.runRequest。还记得上面介绍Target的时候有提到生命周期对Target的影响了吧– 在Target为GifDrawble的时候Target会随着生命周期启动和停止播放动画。

Ok 我们继续看runRequest,上面已经知道request是一个SingleRequest,SingleRequest中启动请求是通过begin方法来完成的。

public void runRequest(Request request) {
//添加请求到列表中
requests.add(request);
if (!isPaused) {
//如果是非暂停状态,那么就启动请求
request.begin();
} else {
//如果是暂停状态那么就加入到pendingRequests中
pendingRequests.add(request);
}
}

begin 方法中会先获取View的尺寸,然后通过onSizeReady传出,实际的加载也是在onSizeReady中进行的。
在开始加载之前先调用onLoadStarted显示占位图片。

public void begin() {
//没有设置Model的情况
if (requestContext.getModel() == null) {
onLoadFailed();
return;
}
//等待分配尺寸
status = Status.WAITING_FOR_SIZE;
//先获取RequestContext 中的宽高,看下是否有效
int overrideWidth = requestContext.getOverrideWidth();
int overrideHeight = requestContext.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
//如果有效那么直接通过回调将其传出
onSizeReady(overrideWidth, overrideHeight);
} else {
//如果无效那么调用之前介绍的getSize来获取View的尺寸
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
//显示占位图片
target.onLoadStarted(requestContext.getPlaceholderDrawable());
}
}

看下onSizeReady,转了大半圈感觉终于走到了正道,之前都是各种做铺垫,在onSizeReady中会通过engine.load进行图片的加载。关于请求的信息都放在了requestContext中。

public void onSizeReady(int width, int height) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
//将状态切换到RUNNING状态
status = Status.RUNNING;
//.............
//表示当前是从内存缓存中加载的,这个用于在onResourceReady返回的时候告诉回调这个资源是来自哪里的。
loadedFromMemoryCache = true;
//使用Engine加载图片
loadStatus = engine.load(requestContext, width, height, this);
//根据最终获取到的资源是否为空来设置loadedFromMemoryCache
loadedFromMemoryCache = resource != null;
}

在看load代码之前我先给大家剧透下整个加载流程,大家可以结合下面的代码注释对细节进行查看:
整个流程如下:

  1. 检查内存缓存,如果内存缓存中有需要的数据那么就直接使用内存缓存中的数据
  2. 如果内存缓存中没有想要的图像数据,那么检查最近的活跃资源(ActiveResources)是否有我们想要的资源
  3. 如果ActiveResources也没有,我们就继续检查最近的加载任务,如果存在将回调添加到正在加载的任务中,
  4. 上面都没办法获取到我们需要的资源那么就,启动新的加载任务开始加载

可能大家都听说过两级缓存,内存缓存,磁盘缓存,但是Glide在这基础上添加了一层活跃资源缓存,那么什么是活跃资源呢?
活跃资源指的是那些不止一次被加载并没有进行过资源释放的图片,一旦被释放,那么该资源则会从近期活跃资源中删除并进入到内存缓存中,但是如果该资源再次从内存缓存中读取,则会重新添加到活跃资源中

public <Z, R> LoadStatus load(RequestContext<?, R> requestContext, int width, int height,
ResourceCallback cb) {

requestContext.setDimens(width, height);
//根据各个属性生成的内存缓存唯一键值
EngineKey key = keyFactory.buildKey(requestContext, width, height);
//使用生成的key从缓存中尝试获取缓存资源
EngineResource<?> cached = loadFromCache(key,requestContext.isMemoryCacheable()/*如果为false那么将会跳过从内存缓存中获取的步骤*/);
if (cached != null) {
//如果缓存中有可用资源,那么将其返回给回调,通知资源已经准备结束
cb.onResourceReady(cached);
return null;
}
//如果内存缓存中没有对应的资源,那么就尝试从ActiveResource 中尝试获取
EngineResource<?> active = loadFromActiveResources(key,requestContext.isMemoryCacheable()/*如果为false那么将会跳过从内存缓存中获取的步骤*/);
if (active != null) {
cb.onResourceReady(active);
return null;
}

//尝试从线程池中获取特定的任务
EngineJob current = jobs.get(key);
if (current != null) {
//找到有正在下载符合当前要求的资源,那么就不重复下载该资源,直接通过添加回调来复用这个资源
current.addCallback(cb);
return new LoadStatus(cb, current);
}

//如果在线程池中没有找到符合要求的特定资源,那么就通过engineJobFactory这个工厂类来生成一个EngineJob,
EngineJob<R> engineJob = engineJobFactory.build(key, requestContext.isMemoryCacheable());
DecodeJob<R> decodeJob = new DecodeJob<>(requestContext, key, width, height, diskCacheProvider, engineJob);
//将engineJob添加到job池中
jobs.put(key, engineJob);
//添加回调
engineJob.addCallback(cb);
//启动decodeJob
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}

我们先看下内存缓存部分:

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
//从缓存中获取EngineResource
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
//从缓存中获取到后将资源通过一个软引用添加到active resource缓存中。
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
return cached;
}

在内存缓存获取到我们所需要的数据后会将其添加到Active Resource中.

private EngineResource<?> getEngineResourceFromCache(Key key) {
//将匹配的数据从缓存中取出
Resource<?> cached = cache.remove(key);
final EngineResource result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
result = (EngineResource) cached;
} else {
result = new EngineResource(cached, true /*isMemoryCacheable*/);
}
return result;
}

接着我们再看下Active Resource 缓存中获取数据的过程:
Active Resource 的缓存数据存在activeResources中,使用弱引用来持有。在内存不足的时候这部分会被gc掉。

private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = null;
WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
if (activeRef != null) {
active = activeRef.get();
if (active != null) {
active.acquire();
} else {
activeResources.remove(key);
}
}
return active;
}

介绍完内存缓存以及Active Resource缓存后我们看下磁盘缓存:
在磁盘缓存开始前会先在当前获取数据的队列中查看是否先前已经启动,如果先前已经启动的话就不重新创建了。这样可以达到复用数据的目的。最后的最后才会启动从磁盘中获取缓存数据的任务。

DecodeJob是一个Runnable的实现类,主要负责从磁盘加载数据,调用start方法后,这个线程就会run起来。
它的作用如下:

  1. 确定数据的加载来源(Resource,Data,Source)
  2. 创建对应来源的DataFetcherGenerator
  3. 执行DataFetcherGenerator 获取数据
public void run() {
try {
//运行加载任务
runWrapped();
} catch (RuntimeException e) {
callback.onLoadFailed();
throw e;
}
}

private void runWrapped() {
switch (runReason) {
case INITIALIZE:
//第一次我们从这里开始分析
stage = getNextStage(Stage.INITIALIZE);//确定资源的加载来源
generator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE://从硬盘获取资源失败 ,尝试重新获取
runGenerators();
break;
case DECODE_DATA://从一个不属于我们的线程中获取数据,然后切换到我们自己的线程中处理数据
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}

我们第一次进入的时候runReason为INITIALIZE,这时候会先通过getNextStage来获取当前阶段的下一阶段。首先在看代码之前我们需要先明白Data和Resource的区别:
Resource:原始的图片(或gif)数据
Data:经过处理(旋转,缩放)后的数据

该方法的大致逻辑如下:

  1. 如果是初始状态,则判断是否解码已缓存的Resource,true是解码Resource。false的话则会通过递归进入第二个判断分支
  2. 判断是否解码已缓存的Data,true是解码Data,false的话则会通过递归进入第三个判断分支
  3. 该阶段则需要从数据源去解码。

简单的来说,就是根据Resource—>Data—>source的顺序去解码加载数据,该阶段Stage的确定,影响着下一阶段DataFetcherGenerator相应子类的实例创建

private Stage getNextStage(Stage current) {
if (current == null) {
return null;
}
DiskCacheStrategy strategy = requestContext.getDiskCacheStrategy();
switch (current) {
case INITIALIZE:
return strategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return strategy.decodeCachedData() ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
return Stage.SOURCE;
default:
return null;
}
}

通过getNextStage已经获取到适当的状态后紧接着就是通过getNextGenerateor生成获取数据的DataFetcherGenerator

private DataFetcherGenerator getNextGenerator() {
if (stage == null) {
return null;
}
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(width, height, diskCacheProvider.getDiskCache(), requestContext, this);
case DATA_CACHE:
return new DataCacheGenerator(requestContext.getCacheKeys(), width, height, diskCacheProvider.getDiskCache(), requestContext, this);
case SOURCE:
return new SourceGenerator<>(width, height, requestContext, diskCacheProvider.getDiskCache(), this);
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}

DataFetcherGenerator使用已注册的ModelLoaders和Model来生成一系列的DataFetcher。有如下实现类
ResourceCacheGenerator:经过处理的资源数据缓存文件(采样转换等处理)
DataCacheGenerator:未经处理的资源数据缓存文件
SourceGenerator:源数据的生成器,包含了根据来源创建的ModelLoader和Model(文件路径,URL…)

下面我们一一来看下这些DataFetcherGenerator

####### ResourceCacheGenerator

public boolean startNext() {
//获取当前的缓存的key列表
List<Key> sourceIds = requestContext.getCacheKeys();
//获取当前注册的Resource class
List<Class<?>> resourceClasses = requestContext.getRegisteredResourceClasses();

while (modelLoaders == null || !hasNextModelLoader()) {
resourceClassIndex++;
if (resourceClassIndex >= resourceClasses.size()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
resourceClassIndex = 0;
}

Key sourceId = sourceIds.get(sourceIdIndex);
Class<?> resourceClass = resourceClasses.get(resourceClassIndex);
Transformation<?> transformation = requestContext.getTransformation(resourceClass);

//生成对应的key
Key key = new ResourceCacheKey(sourceId, requestContext.getSignature(), width, height,transformation,resourceClass, requestContext.getOptions());
//从磁盘缓存中获取对应的缓存文件
cacheFile = diskCache.get(key);
if (cacheFile != null) {
//存在缓存文件
this.sourceKey = sourceId;
//获取ModeLoaders 获取到之后modelLoaders != null 那么就退出循环,这个过程主要是从Register中获取到注册的ModeLoader
modelLoaders = requestContext.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}

fetcher = null;
while (fetcher == null && hasNextModelLoader()) {
//获取ModeLoader
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
//获取Fetcher,如果获取到了就不再循环了
fetcher = modelLoader.buildLoadData(cacheFile, width, height, requestContext.getOptions()).fetcher;
if (fetcher != null) {
//使用Fetcher加载数据
fetcher.loadData(requestContext.getPriority(), this);
}
}
return fetcher != null;
}
DataCacheGenerator
public boolean startNext() {

//第一次调用的时候modelLoaders == null 这里只执行一次
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= sourceIds.size()) {
return false;
}
//获取要加载的数据源的Id
Key sourceId = sourceIds.get(sourceIdIndex);
//生成缓存key
Key originalKey = new DataCacheKey(sourceId, requestContext.getSignature());
//使用key值获取缓存文件
cacheFile = diskCache.get(originalKey);
if (cacheFile != null) {
//如果找到缓存文件,那么使用缓存文件类型来获取已经注册的Model的加载器
this.sourceKey = sourceId;
modelLoaders = requestContext.getModelLoaders(cacheFile);
//重置加载器列表index
modelLoaderIndex = 0;
}
}

fetcher = null;
//如果有一次fetcher != null就停止,如果没有fetcher 并且全部已经遍历结束则退出循环
while (fetcher == null && hasNextModelLoader()) {
//获取ModelLoader
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
//获取fetcher
fetcher = modelLoader.buildLoadData(cacheFile, width, height, requestContext.getOptions()).fetcher;
//使用fetcher获取数据
if (fetcher != null) {
fetcher.loadData(requestContext.getPriority(), this);
}
}
return fetcher != null;
}
SourceGenerator
public boolean startNext() {
//如果已经有缓存数据那么就切换到DataCacheGenerator
if (dataToCache != null) {
cacheData();
dataToCache = null;
}
//如果之前有缓存的话这里的sourceCacheGenerator 为 DataCacheGenerator,那么这里将会调用startNext获取到Fetcher而后调用loadData加载数据。
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}

//如果没有缓存就会继续走到这里
sourceCacheGenerator = null;

loadData = null;
while (loadData == null && hasNextModelLoader()) {
loadData = loadDataList.get(loadDataListIndex++);
if (loadData != null) {
//加载数据
loadData.fetcher.loadData(requestContext.getPriority(), this);
}
}
return loadData != null;
}
private void cacheData() {
try {
//获取对应的编码器
Encoder<Object> encoder = requestContext.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer = new DataCacheWriter<>(encoder, dataToCache, requestContext.getOptions());
//生成DataCacheKey
Key originalKey = new DataCacheKey(loadData.sourceKey, requestContext.getSignature());
//将数据写入到磁盘缓存中
diskCache.put(originalKey, writer);
} finally {
loadData.fetcher.cleanup();
}
//将Generator切换到DataCacheGenerator
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), width, height,
diskCache, requestContext, this);
}

这里需要注意下,SourceGenerator可以根据磁盘缓存策略选择是直接返回还是先写到磁盘再从缓存文件中加载。

public void onDataReady(Object data) {
//根据当前磁盘缓存策略,如果需要对数据进行缓存,那么将数据赋给dataToCache
DiskCacheStrategy diskCacheStrategy = requestContext.getDiskCacheStrategy();
//这个分支是先将数据写入到磁盘缓存,再从缓存文件中加载。
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
// 这时候我们可能在其他线程中进行加载的,但是在做任何操作的时候必须重新切换到Glide线程
cb.reschedule();
} else {
//如果当前缓存策略不要求对数据进行缓存那么直接通过回调返回
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource());
}
}

看完上面的三个DataFetcher后我们看下在DecodeJob中怎么通过这些Fetcher进行获取数据,这就涉及到runGenerators这个方法:
如果获取成功则直接回调onDataFetcherReady,如果失败则通过reschedule重新调度

private void runGenerators() {
currentThread = Thread.currentThread();
while (!isCancelled && generator != null
&& !generator.startNext()/*如果fetch成功获取并且通过通过fetch能够成功获取到数据则返回true这时候不执行while当中的循环*/) {
stage = getNextStage(stage);
generator = getNextGenerator();
if (stage == Stage.SOURCE) {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
return;
}
}
// We've run out of stages and generators, give up.
if (stage == null) {
callback.onLoadFailed();
}
}

看完上面代码密密麻麻的,但是最主要的代码就一句generator.startNext()也就是上面介绍DataFetcher的时候重点注解的那个方法,在那个方法中会调用对应的Fetcher来获取数据。后续的部分会着重介绍一个从网络上获取数据的Fetcher,这里先着重介绍流程。好了我们继续:
不论是哪种Fetcher,获取完数据后都会回调DecodeJob里面的onDataFetcherReady

public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource) {
//各个Generator加载数据结束的时候会到这里
this.currentSourceKey = sourceKey; //对应的数据key
this.currentData = data; //解码前的原始数据
this.currentFetcher = fetcher; //获取数据的fetcher
this.currentDataSource = dataSource;//数据类别
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
//从原始数据中对数据进行解码
decodeFromRetrievedData();
}
}

private void decodeFromRetrievedData() {
//对原始数据进行解码
Resource resource = decodeFromData(currentFetcher, currentData, currentDataSource);
if (resource != null) {
//通过回调进行返回
callback.onResourceReady(resource);
cleanup();
} else {
runGenerators();
}
}

private Resource decodeFromData(DataFetcher<?> fetcher, Data data, DataSource dataSource) {
try {
if (data == null) {
return null;
}
Resource result = decodeFromFetcher(data, dataSource);
return result;
} finally {
fetcher.cleanup();
}
}

private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource) {
//从requestContext中获取当前数据类型的解码器
LoadPath<Data, ?, R> path = requestContext.getLoadPath((Class<Data>) data.getClass());
if (path != null) {
return runLoadPath(data, dataSource, path);
} else {
return null;
}
}

getLoadPath的任务是从注册表中获取特定数据类型,转换类型的图像解码器:

<Data> LoadPath<Data, ?, TranscodeClass> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, getResourceClass(), transcodeClass);
}

这里调用getLoadPath了从loadPathCache中获取对应数据类型的解码器,loadPathCache 是一个缓存,这个大家见怪不怪了:

  public <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode> getLoadPath(
Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {

LoadPath<Data, TResource, Transcode> result = loadPathCache.get(dataClass, resourceClass, transcodeClass);
if (result == null && !loadPathCache.contains(dataClass, resourceClass, transcodeClass)) {

//获取解码器列表
List<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass,transcodeClass);
//有可能从数据类中没法解码或者转换当前类型
if (decodePaths.isEmpty()) {
result = null;
} else {
result = new LoadPath<>(dataClass, decodePaths);
}
//添加到loadPathCache
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}
return result;
}

下面是从注册表中获取符合要求的解码器列表的实现:

private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>> getDecodePaths(
Class<Data> dataClass, Class<TResource> resourceClass, Class<Transcode> transcodeClass) {
//从注册表中获取解码器信息列表
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
List<Class<TResource>> registeredResourceClasses = decoderRegistry.getResourceClasses(dataClass, resourceClass);
for (Class<TResource> registeredResourceClass : registeredResourceClasses) {
//获取图像变换列表
List<Class<Transcode>> registeredTranscodeClasses =
transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);
//获取到对应类型,对应变换的解码器
for (Class<Transcode> registeredTranscodeClass : registeredTranscodeClasses) {
List<ResourceDecoder<Data, TResource>> decoders =
decoderRegistry.getDecoders(dataClass, registeredResourceClass);
//这里就是我们需要的解码器
ResourceTranscoder<TResource, Transcode> transcoder =
transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
//添加到符合要求的解码器列表
decodePaths.add(new DecodePath<>(dataClass, decoders, transcoder));
}
}
return decodePaths;
}

经过层层调用后我们就获取到了当前数据的解码器,拿到解码器后不用说要做的事情就是对数据进行解码了,我们看下这部分内容:

private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path) {
return path.load(data, requestContext, width, height, new DecodeCallback<ResourceType>(dataSource));
}

我们接下来看下LoadPath的load方法,这里最关键的部分就是path.decode这个方法,它就是调用从注册表中获取到的解码器到decode方法对数据进行解码的。

public Resource<Transcode> load(Data data, RequestContext<?, Transcode> context,
int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback) {
Preconditions.checkNotNull(data);

Resource<Transcode> result = null;
Options options = context.getOptions();
DataRewinder<Data> rewinder = context.getRewinder(data);
try {
int size = decodePaths.size();
for (int i = 0; i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
result = path.decode(rewinder, width, height, options, decodeCallback);
if (result != null) {
break;
}
}
} finally {
rewinder.cleanup();
}
return result;
}

到这里为止整个数据获取,解码都完成了,那么我们接下来顺着原路返回,看下怎么将这些经过解码后的图片设置到对应的Target上,我们先回到Engine类:
它有一个叫做onEngineJobComplete的回调,是在上面加载数据,解码数据之后对调的。

public void onEngineJobComplete(Key key, EngineResource<?> resource) {
Util.assertMainThread();
if (resource != null) {
resource.setResourceListener(key, this);
if (resource.isCacheable()) {
activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
}
}
jobs.remove(key);
}

这里处理很简单就是将加载后的数据添加到activeResources,下一次的时候就可以从activeResources中获取了。

还记得前面介绍Engine.load的时候如果从内存缓存以及Active Resource缓存中获取到数据后是怎么处理的吧。是的就是调用:

cb.onResourceReady(cached);

cb 是啥,看代码可以看出SingleRequest,所以我们看下SingleRequest的onResourceReady

@Override
public void onResourceReady(Resource<?> resource) {
Class<R> transcodeClass = requestContext.getTranscodeClass();
if (resource == null) {
onLoadFailed();
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
onLoadFailed();
return;
}
if (!canSetResource()) {
releaseResource(resource);
status = Status.COMPLETE;
return;
}
//这里是关键
onResourceReady((Resource<R>) resource, (R) received);
}

private void onResourceReady(Resource<R> resource, R result) {
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (requestListener == null
|| !requestListener.onResourceReady(result, requestContext.getModel(), target,loadedFromMemoryCache, isFirstResource)) {
//这里是终点,终于快结束了,累死我了。
Transition<? super R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
}

上面终于看到target了。既然都讲到这了我就再回头看下吧,比如我们当前是一个GifDrawable,那么Target就是DrawableImageTarget
我们就再来看下它的onResourceReady

public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
if (!(resource instanceof Animatable) && layoutParams != null && layoutParams.width > 0
&& layoutParams.height > 0) {
resource = new FixedSizeDrawable(resource, layoutParams.width, layoutParams.height);
}
super.onResourceReady(resource, transition);
if (resource instanceof Animatable) {
((Animatable) resource).start();
}
}

它调用了父类也就是ImageViewTarget的onResourceReady,在这里会调用传入的Transition对图像进行一次转换,然后调用setResource设置到对应的Target上。

public void onResourceReady(Z resource, Transition<? super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResource(resource);
}
}

所以总的就是先将图像转换,设置到ImageView上,然后如果是gif就调用start方法开始播放。整个流程结束了,真他妈累。对了好像还忘记给大家介绍HttpUrlFetcher了。实在讲不动了就贴个标有注释的代码给大家吧。

public class HttpUrlFetcher implements DataFetcher<InputStream> {

HttpUrlFetcher(GlideUrl glideUrl, int timeout, HttpUrlConnectionFactory connectionFactory) {
this.glideUrl = glideUrl;
this.timeout = timeout;
this.connectionFactory = connectionFactory;
}

@Override
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
InputStream result = null;
try {
result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
} catch (IOException e) {
}
//将数据通过回调返回
callback.onDataReady(result);
}

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
//重定向次数超过5次
if (redirects >= MAXIMUM_REDIRECTS) {
throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
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.
}
}
//创建HttpURLConnection
urlConnection = connectionFactory.build(url);
//往HttpURLConnection 添加 Head参数
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
//设置超时时间
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
//设置不使用缓存
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
//连接
urlConnection.connect();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
//获取内容大小
String contentLength = urlConnection.getHeaderField(CONTENT_LENGTH_HEADER);
//获取输入流InputStream
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
return stream;
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
}
//从连接中获取Location字段进行重定向
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());
}
}

interface HttpUrlConnectionFactory {
HttpURLConnection build(URL url) throws IOException;
}

private static class DefaultHttpUrlConnectionFactory implements HttpUrlConnectionFactory {
@Override
public HttpURLConnection build(URL url) throws IOException {
//获取HttpURLConnection
return (HttpURLConnection) url.openConnection();
}
}
}
Contents
  1. 1. DataCacheGenerator
  2. 2. SourceGenerator