Glide继承自ComponentCallbacks2,用来监听内存情况。在内存紧张的时候,系统会自动触发ComponentCallbacks2的trimMemory回调。
public void trimMemory(int level) {
memoryCache.trimMemory(level);
bitmapPool.trimMemory(level);
arrayPool.trimMemory(level);
}
问题1: 在Glide里面创建Fragment的时候为什么要用HashMap临时保存创建好的Fragment?
final Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments =
new HashMap<>();
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint) {
SupportRequestManagerFragment current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
原因是为了防止重复添加多个Fragment。比如下面这代码:
void glide(){
Glide.with(context).load(url).into(view);
Glide.with(context).load(url).into(view);
}
当连续调用Glide.with的时候,会多次创建对应的fragment。但是现在他两的生命周期是一样的,所以没必要创建多个。然后由于执行代码:
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
这是一个异步操作,就导致只用 fm.findFragmentByTag(FRAGMENT_TAG);来获取对应的Fragment对象是不准确的,所以需要临时用HashMap保存下,他是同步的。
Glide.with(FullscreenActivity.this)
.load(list.get(cun))
.into(view);
Glide.with作用:创建单例对象Glide。根据当前的生命周期对象创建对应的RequestManger对象Glide.with.load作用:创建RequestBuilder对象。每掉用一次load就会创建一个RequestBuilder对象
RequestManager里面有两个变量,RequsetTracker和TargetTracker,RequestTracker用来保存调用load的时候创建的Request对象。TragetTracker用来保存每次load的时候创建的target对象。为什么要保存呢,因为Glide是具有生命周期的,可以在RequestManager里面直接管理他们。
接着看Glide.with.load方法,这个方法这里一定要看懂才行。
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
Request request = buildRequest(target, targetListener, options, callbackExecutor); // 2
Request previous = target.getRequest(); // 1
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { // 3
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target); // 4
target.setRequest(request); // 6
requestManager.track(target, request); // 5
return target;
}
先来看标记1的地方:
// 这是他的源码
private Object getTag() {
return view.getTag(tagId);
}
再来看下标记6的地方:
private void setTag(Object tag) {
isTagUsedAtLeastOnce = true;
view.setTag(tagId, tag);
}
可以看到Glide将创建的Request对象添加到了View里面。然后在通过get方法获取当前view上一次添加的request对象。
在看标记3的地方你就懂了,他对通一个view进行了request的判断,如果和上次的请求是同一个request的话就不要在重新执行额外的添加操作,可以直接开始requset请求。
如果不是同一个request的话就要看标记4 和 标记5的地方。
标记4:移除当前的target和request对象,因为你这个view现在需要开启一个新的图片请求了,上一个请求没有完成的话也不必在继续请求下去了,现在就要停止下来。
标记5:添加现在新的Request对象,和Target对象。
在load的时候去获取图片大小,然后调用异步的网络请求,然后在调用target.onLoadStarted去设置占位图片。
现在看下是怎么加载出来的。
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
}
}
先来看下这个engine是在什么时候创建的。
private Request obtainRequest(
Object requestLock,
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
Executor callbackExecutor) {
return SingleRequest.obtain(
context,
glideContext,
requestLock,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
可以看到在创建SingleRequest对象的时候通过glideContext.getEngine()创建的。那GlideContext是怎么创建的?答案是在创建RequestBuilder创建的时候创建的:
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class<TranscodeType> transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
apply(requestManager.getDefaultRequestOptions());
}
RequestBuilder又是在我们调用Glide.with().load里面有个as方法创建的。最终这个GlideContext其实就是Glide对象里面创建的:
Glide(
@NonNull Context context,
@NonNull Engine engine,
@NonNull MemoryCache memoryCache,
@NonNull BitmapPool bitmapPool,
@NonNull ArrayPool arrayPool,
@NonNull RequestManagerRetriever requestManagerRetriever,
@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
@NonNull RequestOptionsFactory defaultRequestOptionsFactory,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
@NonNull List<RequestListener<Object>> defaultRequestListeners,
@NonNull List<GlideModule> manifestModules,
@Nullable AppGlideModule annotationGeneratedModule,
@NonNull GlideExperiments experiments) {
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
engine,
experiments,
logLevel);
}
目前可以知道了Glide全局只有一个对象,GlideContext全局只有一个对象,然后Engine也只有一个对象。
看下Engine.load方法,主要做数据请求的是DecodeJob里面的run方法:
class DecodeJob<R>
implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
public void run() {
runWrapped();
}
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
runWrapped方法很关键,他用来查询可以处理的处理器:
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
处理器有三种:
- ResourceCacheGenerator 用来处理磁盘缓存中获取的经过转化后的资源
- DataCacheGenerator 用来处理磁盘里面的原图
- SourceGenerator 用来下载网络图
1和2也就是我们经常说的磁盘缓存。1保存的是经过transform也就是经过变换过的资源。2保存的是未被处理过的图片。也就是原图。
最终通过3进行下载。
Glide缓存有三种:
- 活动缓存
- 内存缓存
- 磁盘缓存
当一个图片已经被其他View在使用的时候,那么这个资源就会被缓存到活动缓存里面,其他view要用的话就直接拿这个活动缓存里面的就行。
活动缓存是通过hashMap的方式进行存储的,键值是弱引用,当触发gc的时候,活动缓存里面的数据会完全被移除,然后添加到内存缓存里面。
总结Glide整个加载流程:
当调用Glide.into的时候,会去创建Request对象。每个用来展示的视图他会保存上一次创建好的Request对象。这个时候就会比较当前的Request对象和当前视图曾经保存的request是一样的就那以前的request进行展示。如果不一样就开启请求。engine.load()。
每次load的时候都会创建key对象。然后通过这个key去找对应的缓存数据。首先从活动缓存拿,然后再从内存缓存里面找:
class Engine{
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
return active;
}
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
return cached;
}
return null;
}
}
活动缓存的内容,已经用专门的一篇博客进行了分析。内存缓存LruResourceCache在另外一篇文章进行了详解。