常用的使用方式如下,特点是链式调用,with/load/into是必不可少的,还可以添加图片数据格式、占位图、错误占位图、缓存机制、网络请求等等配置。
Glide.with(Context)
.load(StringUrl)
.into(ImageView);
总而言之,功能十分强大而不需要自己实现图片的加载、数据的转换、缓存等等。
那么他是怎样帮我们把这些都处理完的呢,看下面解析。
with方法有多个,都是传入Context(Context的子类如:Activity、Fragment,支持support和app包下的两种Fragment,支持FragmentActivity) 或者 View(最终还是通过view获取了Context);
源码大同小异,基本都是调用了{@link Glide#getRetriever(Context)}和{@link RequestManagerRetriever#get()}两个方法;
特别注意,这里的返回值是RequestManager对象,后面会用到!!
// Glide.java
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
// Glide.java
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
//... 检查Context不能为空
return Glide.get(context).getRequestManagerRetriever();
}
// 1. volatile 关键字已经在多线程中分析过,用来保证可见性和部分有序性;
private static volatile Glide glide;
// 2. 可以看出Glide的实例是单例的;
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
// 3. 此方法通过反射创建了一个GeneratedAppGlideModuleImpl的实例
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
// 4. 真正的检查和初始化Glide实例;
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
在上述的源码可以看出:
a. 在获取请求管理索引器对象的过程中,先创建了Glide的实例,并对其进行初始化,Glide是单例的,使用了双重校验锁单例模式;
b. 使用反射,创建了GeneratedAppGlideModuleImpl实例
继续看源码:(这部分可以简略看下或直接看总结)
// Glide.java
private static void checkAndInitializeGlide(
// ... 使用布尔标志判断是否已经初始化
initializeGlide(context, generatedAppGlideModule);
// ...
}
// 1. 终于到核心方法执行初始化操作了, 这期间通过同名方法new了一个GlideBuilder实例
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
...
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
...
// 2. 从清单配置文件中解析出GlideModule列表
manifestModules = new ManifestParser(applicationContext).parse();
...
// 3. 根据是否配置了GlideModule来决定是否需要使用其统一配置的Options,以及统一注册的Componnets
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
? annotationGeneratedModule.getRequestManagerFactory()
: null;
builder.setRequestManagerFactory(factory);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
// 4. 最重要的一步!!!
// 使用GlideBuilder构造器类构造出了Glide实例
Glide glide = builder.build(applicationContext);
for (com.bumptech.glide.module.GlideModule module : manifestModules) {
...
module.registerComponents(applicationContext, glide, glide.registry);
...
}
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
// GlideBuilder.java
// 简化了所有判空和部分传参
// 可以看出build为Glide的创建,初始化了大量引用对象!!!
Glide build(@NonNull Context context) {
sourceExecutor = GlideExecutor.newSourceExecutor();
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
animationExecutor = GlideExecutor.newAnimationExecutor();
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
bitmapPool = new LruBitmapPool(size);
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
engine = new Engine(...);
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(...);
}
在上述的源码可以看出:
a. 检查和初始化方法检查了是否有GlideModule的实现类:
这个类是给Glide做统一配置的。当业务需要多处加载图片使用Glide,为方便配置可自行实现一个GlideModule,并且在AndroidManifest.xml文件中的
继承GlideModule,需要实现applyOptions、registerComponent方法,分别的作用是:做统一配置(如LruCache的策略、DiskLruCache的策略、编码格式、Bitmap池等等);向Glide注册自定义组件(这种可以替换默认的网络请求套件、数据拦截器等)。
b. 实例化了Glide单例:
使用GlideBuilder构造出Glide实例,并在build方法中配置了资源线程池、缓存处理线程池、内存计算器、Bitmap池、连接监控工厂、缓存、加载引擎、请求管理索引器等。
现在回到1.2开头时分析的get方法,这时候的getRequestManagerRetriever方法就是获取的Glide对象中的RequestManagerRetriever对象的引用。
上一步构造Glide并获取Glide中创建的RequestManagerRetriever实例后,继续调用RequestManagerRetriever的get,最后调用下面的方法:
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
// build!
requestManager = factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
这个方法很简单:
就是获取RequestManager对象或者用工厂构造它,其中传入了current.getGlideLifecycle,看名字大胆猜测是用于Activity或者Fragment的生命周期管理的!事实也正是这样。
1. with方法解析了清单配置文件中的统一选项配置、组件注册类GlideModule,并把统一配置放入了GlideBuilder,注册组件放入了Glide的Register;
2. with方法用GlideBuilder构造了Glide的单例,初始化了缓存、请求管理、线程池、资源池、加载引擎等重要对象;
3. with方法最终返回了管理请求、生命周期的RequestManager实例。
load方法分成了两步,一是as,一是load。as方法就是创建了RequestBuilder对象。
// RequestManager.java
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
发现load方法几乎没干啥,就是给model赋值,比如传入一个字符串型的url,就吧这个字符串赋给了model。然后离了个flag表明已经model赋值了。
// RequestBuilder.java
@Nullable private Object model;
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
// RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
...
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
...
// 1. 核心方法:buildRequest
Request request = buildRequest(target, targetListener, options, callbackExecutor);
// 2. 对比相同的ImageView之前的请求有啥不同的
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
// 3. 不同的请求才进入这里,整整设置了请求并开始追踪任务
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
上述代码:
a. into最终构造了请求,通过buildRequest方法,构造了Request对象,并且根据情况构造了缩略图Request、错误Request对象;
b. 避免了相同请求再次被追踪,不同请求则被执行了。
找到执行请求的语句:
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
很容易看出:runRequest就是关键锁在了!!!
Request是一个接口,所以begin等方法需要到SingleRequest实现类。
基本流程就分析到这,再深入继续往下:
真正的执行请求方法:SingleRequest.begin:
// SingleRequest.java
@Override
public void begin() {
synchronized (requestLock) {
...
if (model == null) {
// ... 之前赋值过model的,如果是空抛异常
}
...
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
// 等待尺寸准备好
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
...
}
}
@Override
public void onSizeReady(int width, int height) {
...
loadStatus = engine.load(.../**此处省略N个参数*/);
...
}
生活终于对小猫咪下手了:
RequestBuilder.into --> RequestManager.track --> Request.begin --> SingleRequest.begin --> SingleRequest.onSizeReady --> Engine.load
public <R> LoadStatus load(...) {
// 1. 根据图片的url、签名、尺寸、选项等生成一个key
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
// 2. 从内存中加载图片资源
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
// 3. 内存没有,从磁盘加载或者网络获取
if (memoryResource == null) {
return waitForExistingOrStartNewJob(...);
}
...
}
}
从load方法中可以看到两个重要的步骤:
继续深入loadFromMemory和waitForExistingOrStartNewJob:
private EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
// 1. 如果设置的缓存策略是不缓存,那么直接返回空
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
...
// 2. 从loadFromActiveResources资源引用队列中获取图片,非空则返回
return active;
}
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
...
// 3. 从loadFromCache内存缓存中获取图片,非空则返回
return cached;
}
return null;
}
这里可以很清晰的看出Glide图片缓存的其中两级:一级:引用队列缓存;二级:内存缓存
来看下具体是怎么缓存的以及缓存所用的数据结构:
loadFromActiveResources方法调用了ActiveResources的get方法
// ActiveResources.java
// 1. 定义了一个HashMap的成员变量用来存储图片资源的弱引用;
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
// 2. 其中HashMap的Value数据结构是一个弱引用
static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {...}
// 3. 使用HashMap的get方法获取图片弱引用
synchronized EngineResource<?> get(Key key) {
// 从HashMap中获取资源弱引用
ResourceWeakReference activeRef = activeEngineResources.get(key);
// 弱引用为空,返回空
if (activeRef == null) {
return null;
}
// 从弱引用中获取资源强引用
EngineResource<?> active = activeRef.get();
// 资源强引用为空,清除key并返回空;资源强引用不为空,返回资源强引用
if (active == null) {
cleanupActiveReference(activeRef);
}
return active;
}
一级缓存: 一个存储活跃图片资源弱引用的HashMap!
1. 将active的图片的弱引用存储在一个HashMap中(Value是一个EngineResource的弱引用);
2.通过WeakReference.get方法获取图片资源。
下面看二级缓存:
loadFromCache方法
// Engine.java
private EngineResource<?> loadFromCache(Key key) {
// 1. 从缓存中获取,实际是调用cache的remove方法返回EngineResource
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
// 2. 然后将EngineResource重新put到队列中(回到了一级缓存中);
activeResources.activate(key, cached);
}
return cached;
}
// 3. MemoryCache是一个接口,其实现类是LruResourceCache
private final MemoryCache cache;
private EngineResource<?> getEngineResourceFromCache(Key key) {
// 4. 从LRU缓存中移除Key对应的资源并返回被移除的资源;
Resource<?> cached = cache.remove(key);
//... 进行非空判断和类型转换
return cached;
}
二级缓存:一个LruCache!
1. 使用LruCache缓存数据,当从二级缓存中找到资源,则从二级缓存移除并返回该资源;
2. 二级缓存中被移除的资源最后被放入了一级缓存HashMap,并以弱引用存在。
下面看三级缓存:
// Engine.java
private <R> LoadStatus waitForExistingOrStartNewJob(...) {// 省略参数列表
// 1. 从HashMap中查找是否曾经执行过相同的任务
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
// 不为空说明原来执行过加载任务,只需要从磁盘中重新加载即可
current.addCallback(cb, callbackExecutor);
...
return new LoadStatus(cb, current);
}
// 2. 未执行过此图片加载任务,创建任务并放入HashMap保存,然后使用引擎启动任务
EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache);
DecodeJob<R> decodeJob = decodeJobFactory.build(glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob);
...
return new LoadStatus(cb, engineJob);
}
三级缓存:返回一个LoadStatus对象
1. 先判断是否执行过加载任务,如果执行过,说明在磁盘中有缓存,去磁盘decode图片资源;
2. 若没有加载过,则创建新的加载任务,并放入任务HashMap,然后执行网络加载。
上一节进入了Engine.load根据不同的情况:
最终是怎样加载资源的呢?
综上所述,资源加载,最终是由引擎(Engine.start)启动任务,并执行解码任务(DecodeJob.run),任务由数据访问生成器(DataFetcherGenerator.startNext)启动下一个任务方法中,有模型加载器(ModelLoader.LoadData)的数据获取器(DataFetcher.loadData)执行;
具体由哪个DataFetcher,哪个ModelLoader执行,由Register的配置根据数据类型等决定。
这篇文章分析的较为详细:(引用文章,侵权请联系删除!)
Glide的生命周期
总结起来就是在Glide.with方法被调用时,传入的Context,如Activity\Fragment等,会创建或者从HashMap中获取一个RequestManagerFragment的实例,此实例持有ActivityFragmentLifecycle对象,其中RequestManagerFragment继承自Fragment,有Fragment的生命周期,在Fragment onStart、onStop、onDestroy触发时,调用ActivityFragmentLifecycle的相应方法,此时在即可从ActivityFragmentLifecycle获取生命周期的触发时机。
特别地,Activity中没有Fragment时,RequestManagerFragment会通过onAttach方法为Activity添加一个不可见的Fragment。
请求管理器RequestManager关联了此RequestManagerFragment实例(被观察者),并作为其Fragment生命周期的观察者,因此可以知晓生命周期,再做出对资源加载请求的启动、暂停、终止。