今天要和大家一起分析的是图片加载开源库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();
}
}
}

1. 在项目中引入Dagger2

Dagger2 搞Android开发以及Java开发的同学估计即使没有使用过也应该听说过这个开源库吧。它是一个依赖注入库.源码地址如下所示:
Dagger2 github地址

在项目中引入Dagger2
project的build.gradle添加

dependencies {
... // 其他classpath
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //添加apt命令
}

module的build.gradle添加

// 添加其他插件
apply plugin: 'com.neenbedankt.android-apt'//添加apt命令
dependencies {
apt 'com.google.dagger:dagger-compiler:2.0.2' //指定注解处理器
compile 'com.google.dagger:dagger:2.0.2' //dagger公用api
provided 'org.glassfish:javax.annotation:10.0-b28' //添加android缺失的部分javax注解
}

2. Dagger2简介

在介绍如何使用Dagger2之前我们必须先搞清楚一件事–为甚么需要使用Dagger2,也就是Dagger2的功能,刚刚提到了Dagger2 是一个依赖注入框架,那么什么是依赖注入呢?为什么要使用依赖注入呢?

我们传统的开发过程中如果某个类中需要一个依赖可以通过在这个类中创建需要的依赖,这样的缺点显而易见,比方我们需要在一个已经开发成熟的项目中更换某个类的实现,再具体点,比如我们在
现有项目中使用的是Picasso作为图像处理框架,但是某天项目中需要显示gif,这时候我们可能考虑到Glide库能够支持Gif播放,所以我们想要将图像处理框架更换为Glide
如果原先没有设计好的话可能改动的代码就很庞大。而且极为容易出错。但是如果使用了依赖注入框架,那么这个问题就变得很简单了,只需要修改对应的Module就可以了,对代码不需要大幅的改动。(这里的前提条件是这两个库接口上是相似的)

那么什么是依赖注入呢?这里我就谈谈自己对依赖注入的理解:
依赖注入就是将使用依赖的代码部分与依赖生成的部分分开,这样做的好处就是:因为对象是在一个独立、不耦合的地方初始化,所以当注入抽象方法的时候,我们只需要修改对象的实现方法,而不用大改代码库。并且可以通过注入这些依赖的Mock对象来进行模拟测试。从而使得对项目的测试更加方便。

有Dagger2就说明有Dagger的存在,关于这段历史大家如果感兴趣的话可以去网上了解下,这里就不展开介绍了,Dagger2的最大改进就是,它使用了Java注解处理器,完全去除了反射机制,在编译的时候检查并分析依赖关系。使得在效率上得到很大的提升。

3. Dagger2结构

下面是Dagger2的大致结构:

整个依赖注入体系分成三个部分:

  1. 依赖提供方: 用于生成并提供依赖对象的一方。
  2. 依赖需求方: 需要使用注入依赖的一方
  3. 依赖注入器: 连接依赖提供方和依赖需求方的注入器。它负责将依赖提供方生成的依赖对象注入到依赖需求方。换个角度来说就是依赖需求方需要注入依赖的时候可以顺着这个注入器找到依赖提供方。

4. Dagger2 重要的注解

了解了大体的结构后我们就需要了解下Dagger2中常用的一些注解,其实Dagger2的注解并不太多,但是需要注意的是这些注解的理解。

@Inject:

我们看到上图中依赖注入器左右两端各有一个@Inject注解也就是说@Inject既可以用在提供方也可以用在依赖需求方,用在依赖提供方的时候一般用来注解待注入对象的构造函数,用在依赖需求方的时候一般用来注解需要Dagger2进行依赖注入的成员变量。

@Module:
我们看到依赖提供方还有个@Module注解,它的作用是什么呢?我们知道提供方已经有了一个@Inject为什么还需要@Module呢?我们考虑一个情景我们现有项目中使用了第三方的类库,在不采用导入第三方类库源码或者源代码非开源的情况,如果用Inject要怎么处理,根本不可能使用Inject注解加入这些类中是吧,那这还怎么办呢?这时候就需要@Module出场了。Modules类是由一系列专门提供依赖的方法组成,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的依赖。

那么这两种方式有没优先级区分呢?
有的,它的查找规则如下:

  1. 首先会先从Module的@Provides方法集合中查找
  2. 如果查找不到,则查找成员变量类型是否有@Inject构造方法。
    也就是说@Module中的优先级会比@Inject注解的构造方法优先级更高,还有个需要注意的是@Modules不仅仅只用于那些第三方项目中不可见源码的对象注入,可以使用@Inject注入的对象,使用@Module一样可以注入。
@Provide

提到@Module就不得不提到 @Provide,我们用这个注解来告诉Dagger2被这个注解的方法是被用来提供依赖的,具体提供哪种依赖对象是由返回值决定的,一般这类方法规定以provide作为开头,后面的可以随意。Module中@Provides方法可以是带输入参数的方法,其参数由Module集合中的其他@Provides方法提供,或者自动调用构造方法,也就是说如果找不到@Provides方法提供对应参数的对象,Dagger2就会自动调用带@Inject参数的构造方法生成相应对象。

下面是一个最基本的@Module的写法

@Module
//1 注明本类属于Module
public class FruitModule{
@Provides
//2 注明该方法是用来提供依赖对象的特殊方法
// 返回值(被依赖的类类型)
// 方法名(provideXxx必须以provide开头,后面随意)
public Fruit provideFruit(){
return new Apple(Color.RED,Size.BIG);
}
}

@Named

如果待注入方需要依赖同个类的两种不同的对象的时候,那要怎么办,我们可能会想就写两个@Provides方法,而且这两个@Provides方法都是返回需要的类型,但是我们前面提到过Dagger2是靠返回值的类型来判断具体选择哪个@Provide方法来提供依赖的,现在有两个provide方法返回同一个类型,那就比较尴尬了,这种现象也有专门的叫法叫做注入迷失,为了解决这个问题这就需要使用@Named来进行区分了:

@Module
public class FruitModule{
@Named("typeA")
@Provides
public Fruit provideApple(){ //提供Apple给对应的mFruitA
return new Apple();
}
@Named("typeB")
@Provides
public Fruit provdeBanana(){ //提供Banana给对应的mFruitB
return new Banana()
}
}

在待注入方也要使用@Named来标记到底使用的是哪个依赖,具体的待注入方以及注入会在下面进行介绍。

@Named("typeA") //添加标记@Name("typeA"),只获取对应的@Name("typeA")的依赖   @Inject
Fruit mFruitA;
@Named("typeB") //添加标记@Name("typeA"),只获取对应的@Name("typeA")的依赖 @Inject
Fruit mFruitB;

@Qualifier

上面的方式只能使用字符串作为区分标签,一般来说是够用的,但是如果你需要其他的方式作为区分标签可以使用Qualifier进行定义了:

@Qualifier   //必须,表示IntNamed是用来做区分用途
@Documented //规范要求是Documented,当然不写也问题不大,但是建议写,做提示作用
@Retention(RetentionPolicy.RUNTIME) //规范要求是Runtime级别
public @interface IntNamed{
int value();
}

用法和@Named注解类似就不展开介绍了。

@Component:

Components从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。它注释的类必须是接口或抽象类。
既然它是注入器,必定由两个部分构成,一个是提供方,一个是需求方,提供方是由module引入,需求方是由inject方法引入。Component的职责就是在inject目标中有使用@Inject
注解的成员变量的时候顺着 Component 所管理的 Module中进行查找需要的依赖,但是如果不需要@ Module那么就不需要定义Component了,也就是说Component 是用于管理 @Module的,可以通过Component中的modules属性把Module加入Component,modules可以加入多个Module。
这样Component获取依赖时候会自动从多个Module中查找获取,需要注意的是Module间不能有重复方法,不然也会照成上面所提到的依赖迷失。

添加多个module有两种方法

1. @Component(modules={××××,×××}) 
2. @Module(includes={××××,×××})这种方法一般用于构建更高层的Module时候使用

假设ComponentA依赖ComponentB,B必须定义带返回值的方法来提供A缺少的依赖
ComponentA依赖ComponentB的代码如下

//定义ComponentB
@Component(modules={××××××××})
interface ComponentB{
...
}

//定义ComponentA
@Component(dependencies={ComponentB.class},modules={××××××××})//使用dependencies
interface ComponentA{
...
}

这样,当使用ComponentA注入Container时,如果找不到对应的依赖,就会到ComponentB中查找。但是,ComponentB必须显式把这些A找不到的依赖提供给A。怎么提供呢,只需要在ComponentB中添加方法即可,如下

@Component(modules={××××××××})
interface ComponentB{
// 假设A中module中找不到apple,banana,oranges,但是B的module有,B必须提供带返回值的方法如下
Apple apple();
Banana banana();
Oranges oranges();
}

Component 当中定义的方法可以分成两类:

  1. 注入的目标对象以injectXXX作为方法名开始, 同一个Component可以有多个inject方法,也即是说可以注入到多个目标对象。注意inject的参数不能是父类,必须是你注入的那个类,因为这里写啥,Dagger就回去对应的类中寻找@Inject注解进行注入
  2. 需要暴露给依赖components的方法,如果不在这里列出那么使用dependencies方式的时候就不会暴露出来。

下面是一个最基本的Component 定义方法

@Component(modules={FruitModule.class})     //指明Component在哪些Module中查找依赖
public interface FruitComponent{ //接口,Dagger2框架将自动生成Component的实现类,对应的类名是Dagger×××××
void inject(Container container); //注入方法,在待注入容器中调用
}

@Component的注入

Component注入有两种方式:

  1. 基本方式
public Container{
public void init(){
DaggerFruitComponent.create().inject(this); //使用FruitComponent的实现类注入
}
}

上面简单例子中,当调用DaggerFruitComponent.create()实际上等价于DaggerFruitComponent.builder().build()。在构建的过程中,默认使用Module无参构造器产生实例。
如果需要传入特定的Module实例,可以使用

DaggerFruitComponent.builder()
.moduleA(new ModuleA()) //指定Module实例
.moduleB(new ModuleB())
.build()

如果Module只有有参构造器,则必须显式传入Module实例。

这里还留有一个问题等到讲 Component 依赖以及子Component的时候讲,那就是在Component依赖以及子Component的情况下怎么进行依赖注入。

@Scope && @Singleton

在学Dagger2的时候最难理解的部分就是@Scope 以及 Component依赖,子Component.还有就是如何在项目中组织Component。
我们接下来先来看下@Scope的作用,在不使用@Scope 的时候我们的例子如下,我们注入到MainActivity后将这两个对象打印出来,

public class Apple {
}

@Module
public class FruidModule {
@Provides
public Apple provideApple() {
return new Apple();
}
}
@Component(modules = {FruidModule.class})
public interface FruidComponent {
void inject(MainActivity activity);
}
@Inject
Apple apple1;

@Inject
Apple apple2;

@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerFruidComponent.builder().build().inject(this);
LogManager.getInstance().info(apple1.toString());
LogManager.getInstance().info(apple2.toString());

}

打印出来的结果如下:

07-18 20:24:15.355 3059-3059/com.idealist.tbfungeek.mvpframework I/MainActivity: class -> MainActivity method -> onCreate() line -> 29 [ Message ] com.idealist.tbfungeek.mvpframework.Apple@3dd96071
07-18 20:24:15.355 3059-3059/com.idealist.tbfungeek.mvpframework I/MainActivity: class -> MainActivity method -> onCreate() line -> 30 [ Message ] com.idealist.tbfungeek.mvpframework.Apple@1956ae56

接着我们再做个对比实验:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface FruidScope {
}
@Module
public class FruidModule {
@FruidScope
@Provides
public Apple provideApple() {
return new Apple();
}
}
@FruidScope
@Component(modules = {FruidModule.class})
public interface FruidComponent {
void inject(MainActivity activity);
}
@FruidScope
@Inject
Apple apple1;

@FruidScope
@Inject
Apple apple2;


@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BaseApplication.getAppDelegate().getRefWatcher().watch(this);
DaggerFruidComponent.builder().build().inject(this);
LogManager.getInstance().info(apple1.toString());
LogManager.getInstance().info(apple2.toString());

}

结果如下:

07-18 20:27:36.364 5818-5818/? V/ActivityLifeCycleManager$1: class -> ActivityLifeCycleManager$1 method -> onActivityCreated() line -> 61 [ Message ] onCreate --> MainActivity
07-18 20:27:36.388 5818-5818/? I/MainActivity: class -> MainActivity method -> onCreate() line -> 28 [ Message ] com.idealist.tbfungeek.mvpframework.Apple@3dd96071
07-18 20:27:36.388 5818-5818/? I/MainActivity: class -> MainActivity method -> onCreate() line -> 29 [ Message ] com.idealist.tbfungeek.mvpframework.Apple@3dd96071

发现了什么没?我们在没有用scope注解的时候两个对象实际是不同的两个对象,但是如果用scope注解标记后两个返回的是同一个对象。

最早看到Singleton注解的时候我第一反应就是只要用上这个注解就可以实现单例模式了,但是它并非我们通常以为的单例,Java中,单例通常保存在一个静态域中,这样的单例往往要等到虚拟机关闭时候,该单例所占用的资源才释放。但是,Dagger通过Singleton创建出来的单例并不保持在静态域上,而是保留在Component实例中。也就是这种单例只是针对对应的Component。如果要实现传统意义上的单例模式,那么就需要通过一定的方法保证对应的Component是全局单例的。

下面是来自网络上的一个很经典的例子,估计看过后大家一定会豁然开朗:

在实际开发中我们可能还需要一种局部单例的控件(这个应该是更常用),比如说我们有三个Activity,MainActivity,BActivity和CActivity,我们想让MainActivity和BActivity共享同一个实例,而让CActivity获取另外一个实例,这又该怎么实现呢?在Dagger2中,我们可以通过自定义Scope来实现局部单例。那就动手吧:
首先让我们先来定义一个局部作用域:

@Scope  
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}

然后在我们的UserModule和ActivityComponent中应用该局部作用域:

@Module  
public class UserModule {
@Provides
@UserScope
User providesUser() {
return new User();
}
}

@UserScope  
@Component(modules = UserModule.class)
public interface ActivityComponent {
void inject(MainActivity activity);

void inject(BActivity activity);
}

大家注意,我的ActivityComponent作为一个注入器只可以向MainActivity和BActivity两个Activity中注入依赖,不可以向CActivity中注入依赖。最后,要让该局部作用域产生单例效果,需要我们在自定义的Appliation类中来初始化这个Component,如下:

public class MyApp extends Application {  
ActivityComponent activityComponent;
@Override
public void onCreate() {
super.onCreate();
activityComponent = DaggerActivityComponent.builder().userModule(new UserModule()).build();
}

ActivityComponent getActivityComponent(){
return activityComponent;
}
}

接下来我们在MainActivity和BActivity中注入依赖,MainActivity如下:

@Inject  
User user;
@Inject
User user2;
private TextView tv;
private TextView tv2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((MyApp) getApplication()).getActivityComponent().inject(this);
tv = ((TextView) findViewById(R.id.tv));
tv2 = ((TextView) findViewById(R.id.tv2));
tv.setText(user.toString());
tv2.setText(user2.toString());
}

BActivity如下:

@Inject  
User user;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
((MyApp) getApplication()).getActivityComponent().inject(this);
TextView tv = (TextView) findViewById(R.id.tv);
tv.setText(user.toString());
}

那么如果我还想在CActivity中使用User对象该怎么办呢?再来一个CUserModule和CActivityComponent呗!
CUserModule如下:

@Module  
public class CUserModule {
@Provides
User providesUser() {
return new User();
}
}

这里我没有再注明单例了哦!
CActivityComponent如下:

@Component(modules = CUserModule.class)  
public interface CActivityComponent {
void inject(CActivity activity);
}

在CActivity中注入依赖:

@Inject  
User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_c);
DaggerCActivityComponent.builder().cUserModule(new CUserModule()).build().inject(this);
TextView tv = (TextView) findViewById(R.id.tv);
tv.setText(user.toString());
}

大家看到,MainActivity和BActivity是同一个实例,而CActivity则是另外一个实例。

同时还需要注意一点就是:一个@Module和component中可以有多个scope对象。这些scope将一个component划分成多个不同的区域:

@Module
public class FruidModule {
@FruidScope
@Provides
public Apple provideApple() {
return new Apple();
}
@FruidScope1
@Provides
public Pear providePear() {
return new Pear();
}
}
@FruidScope1
@FruidScope
@Component(modules = {FruidModule.class})
public interface FruidComponent {
void inject(MainActivity activity);
}
@FruidScope
@Inject
Apple apple1;

@FruidScope
@Inject
Apple apple2;


@FruidScope1
@Inject
Pear apple3;


@FruidScope1
@Inject
Pear apple4;


@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BaseApplication.getAppDelegate().getRefWatcher().watch(this);
DaggerFruidComponent.builder().build().inject(this);
LogManager.getInstance().info(apple1.toString());
LogManager.getInstance().info(apple2.toString());
LogManager.getInstance().info(apple3.toString());
LogManager.getInstance().info(apple4.toString());
}

输出结果:

07-18 21:09:07.245 5676-5676/? I/MainActivity: class -> MainActivity method -> onCreate() line -> 36 [ Message ] com.idealist.tbfungeek.mvpframework.Apple@3dd96071
07-18 21:09:07.245 5676-5676/? I/MainActivity: class -> MainActivity method -> onCreate() line -> 37 [ Message ] com.idealist.tbfungeek.mvpframework.Apple@3dd96071
07-18 21:09:07.245 5676-5676/? I/MainActivity: class -> MainActivity method -> onCreate() line -> 38 [ Message ] com.idealist.tbfungeek.mvpframework.Pear@1956ae56
07-18 21:09:07.246 5676-5676/? I/MainActivity: class -> MainActivity method -> onCreate() line -> 39 [ Message ] com.idealist.tbfungeek.mvpframework.Pear@1956ae56

在使用scope的时候我们还需要注意如下两点:

  1. 编译器会检查 Component管理的Modules,若发现标注Component的自定义Scope注解与Modules中的标注创建类实例方法的注解不一样,就会报错。所以Component和Modules中的scope必须匹配。

  2. 如果两个Component间有依赖关系,那么它们不能使用相同的Scope。

@Subcomponent && dependencies

如果一个Component的功能不能满足你的需求,我们需要对它进行拓展,这时候有两种方法

  1. 使用Component(dependencies=××.classs)
  2. 使用@Subcomponent,Subcomponent用于拓展原有component。这时候注意子component同时具备两种不同生命周期的scope。子Component具备了父Component拥有的Scope,也具备了自己的Scope。

那么它们的不同之处在哪里呢?@Component 只能获取到依赖的 Component 所暴露出来的对象,而 @Subcomponent 则可以获取到父类所有的对象。

Subcomponent其功能效果优点类似component的dependencies。但是使用@Subcomponent不需要在父component中显式添加子component需要用到的对象,只需要添加返回子Component的方法即可,子Component能自动在父Component中查找缺失的依赖。


//父Component:
@PerApp
@Component(modules=××××)
public AppComponent{
SubComponent subcomponent(); //1.只需要在父Component添加返回子Component的方法即可
}

//子Component:
@PerAcitivity //2.注意子Component的Scope范围小于父Component
@Subcomponent(modules=××××) //3.使用@Subcomponent
public SubComponent{
void inject(SomeActivity activity);
}

//使用
public class SomeActivity extends Activity{
public void onCreate(Bundle savedInstanceState){
...
App.getComponent().subCpmponent().inject(this);//4.调用subComponent方法创建出子Component
}
}

通过Subcomponent,子Component就好像同时拥有两种Scope,当注入的元素来自父Component的Module,则这些元素会缓存在父Component,当注入的元素来自子Component的Module,则这些元素会缓存在子Component中。

5. 参考文章

下面是较好的文章,如果看了该博客还是不大明白可以通过下面的文章来进一步阅读

  1. “一盘沙拉”带你入门Dagger2

  2. Android常用开源工具-Dagger2入门

  3. 从零开始的Android新项目4 - Dagger2篇

  4. 解锁Dagger2使用姿势
    Android:dagger2让你爱不释手-重点概念讲解、融合篇