上一节我们解析了为什么Glide能监听页面的生命周期而保证及时回收请求和其他资源,有需要的话看下上一节:
上节我们讲到了Glide.with(this).load(url).into(imageView)前面两个方法,这一节我们看下最重要的一环into方法,下面先看下into方法的代码:
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
我们看到into方法的返回值是一个ViewTarget,ViewTarget是对imageview的一个封装,我们后面会讲到,这里第一二行则是判断是否在主线程和做的一个非空判断,我们看后面的代码,这里的if语句主要是对ScaleType进行设置,获取到requestOptions并且设置ScaleType,我们看下下面的return语句的into方法,第三个字段看得出来传入了一个线程池,是不是觉得图片请求快开始了呢,我们先看下glideContext.buildImageViewTarget方法:
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
这里可以看出,我们将imageView传入imageViewTargetFactory的build方法中,而这是一个工厂方法,针对不同的加载类型生成不同的target,target这里已经与imageView同在,我们返回看下刚才的into方法:
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options, Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
我们直接看buildRequest方法,看命名方式可以猜出,这里应该是创建一个请求了,这里的代码比较长,我做了一个简化:
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
return buildRequestRecursive(
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
private Request buildRequestRecursive(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
.......
Request mainRequest =
buildThumbnailRequestRecursive(
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
if (errorRequestCoordinator == null) {
return mainRequest;
}
.......
.......
}
.......
.......
private Request obtainRequest(
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,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory(),
callbackExecutor);
}
private synchronized void init(
Context context,
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
BaseRequestOptions<?> requestOptions,
int overrideWidth,
int overrideHeight,
Priority priority,
Target<R> target,
RequestListener<R> targetListener,
@Nullable List<RequestListener<R>> requestListeners,
RequestCoordinator requestCoordinator,
Engine engine,
TransitionFactory<? super R> animationFactory,
Executor callbackExecutor) {
this.context = context;
this.glideContext = glideContext;
this.model = model;
this.transcodeClass = transcodeClass;
this.requestOptions = requestOptions;
this.overrideWidth = overrideWidth;
this.overrideHeight = overrideHeight;
this.priority = priority;
this.target = target;
this.targetListener = targetListener;
this.requestListeners = requestListeners;
this.requestCoordinator = requestCoordinator;
this.engine = engine;
this.animationFactory = animationFactory;
this.callbackExecutor = callbackExecutor;
status = Status.PENDING;
if (requestOrigin == null && glideContext.isLoggingRequestOriginsEnabled()) {
requestOrigin = new RuntimeException("Glide request origin trace");
}
}
前面的省略号表示不重要的信息,最终调用到的是init的方法,这里为请求request初始化了很多参数,例如我们前面设置过的model,就是请求的URL,还有是否重新设定了加载的宽高,优先级,动画等等设置,这里就是对请求request的初始化,我们回到最初的into方法:
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options, Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
buildRequest下面一行,通过target获取了一个previous 的request,随后调用了requestManager.clear(target),这里是保证每次请求都是最新的参数和设置,清除上一个请求。这里的 target.setRequest(request);我们点进去发现调用的是setTag方法,将view和request绑定。最后调用了requestManager.track(target, request),还记得前面我们为什么说RequestManager管理target和request么,我们进track方法看下代码:
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
这里还使用的是同步方法,这里持有了两个追踪器,一个是target,一个是request,进track方法看里面的内容:
private final Set<Target<?>> targets =
Collections.newSetFromMap(new WeakHashMap<Target<?>, Boolean>());
public void track(@NonNull Target<?> target) {
targets.add(target);
}
@Override
public void onStart() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onStart();
}
}
@Override
public void onStop() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onStop();
}
}
@Override
public void onDestroy() {
for (Target<?> target : Util.getSnapshot(targets)) {
target.onDestroy();
}
}
我们发现targetTracker维护了一个set集合,用于存储和管理所有的target对象,下面的几个方法,也正是同步于fragment的和requestmanager的生命周期方法,便于在出发生命周期时对所有的target对象执行操作。我们再来看下requestTracker的runRequest方法,看方法命名是不是猜到快要执行网络请求了呢,来看下代码:
private final Set<Request> requests =
Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private final List<Request> pendingRequests = new ArrayList<>();
private boolean isPaused;
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);
}
}
requestTracker同样维护了一个requests Set集合管理所有请求。isPaused参数表示如果暂停,那么当前的request添加到一个等待集合中延迟执行,否则直接执行begin方法,我们继续看下begin方法:
@Override
public void begin() {
synchronized (requestLock) {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
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());
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
}
这里调用的是SingleRequest的begin方法,前面是对当前request运行状态的判断,status = Status.WAITING_FOR_SIZE这里直接赋值,表示当前需要等待获取到view的宽高才能进行下一步,如果你在前面的设置中设置过override方法传递了新的宽高,那么这里直接使用你传入的宽高,否则调用target.getSize(this)方法去获取图片所加载的view的宽高,我们跟进去看这个getsize方法:
void getSize(@NonNull SizeReadyCallback cb) {
int currentWidth = getTargetWidth();
int currentHeight = getTargetHeight();
if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
return;
}
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}
我们发现走到了ViewTarget的getSize方法,ViewTarget正是当初我们封装imageview的target对象,这里直接去获取getTargetWidth、Height,我们不进去看了,就是拿到我们之前传递的image view,获取他的宽高。这里的isViewStateAndSizeValid方法则是判断获取的宽高是否大于0 并且真实可用,可用就表示当前的image view其实已经绘制完成,我们获取的到它的宽高并且直接返回。如果不可用,那么就给image view添加一个绘制的监听addOnPreDrawListener,当绘制完成时我们再去获取它的宽高并且返回。
我们再回到前面的target.getSize方法,继续看下去,宽高获取完成后,调用的是onSizeReady,那么我们跟进去看下:
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
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);
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
}
这里的status 直接赋值为RUNNING状态了,下面一行的sizeMultiplier变量,还记得我们第一节的Glide的常规使用吗,它作用域缩略图,表示我们想将原图缩放几倍大小,这里是获取设置并且计算新的缩略图宽高,我们不去细究,看下面的重点,engine.load方法。可以看出,这个方法传递了很多参数和设置,包括参数设置广泛的requestOptions,这里应该即将开始加载请求的信息了,我们继续看下去:
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}
重点来了,看第一行的 keyFactory.buildKey方法,返回值是一个EngineKey,这个key就是我们用于缓存的key,再看看入参的几个字段。model,signature,width,height,options等。由此可见,影响一张缓存的图片,不只是他的url,还包括了要加载的图片宽高、签名和设置等,所以当同一个图片的URL,如果我们加载不同的宽高,那么这个URL是会缓存两次的。
我们继续看有一个memoryResource对象,并且调用了loadFromMemory方法,我们跟进去看一下:
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return active;
}
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return cached;
}
return null;
}
第一行我们看到了一个isMemoryCacheable,这个就是我们设置的skipMemoryCache,如果没有设置,那么默认就是可用的,这里如果设置了跳过内存缓存,那么直接执行return语句。继续看这里有两个loadFrom方法,一个是loadFromActiveResources,另一个是loadFromCache,那么两个有什么区别呢?其实两个都是作为内存缓存存在的,一个内部维护的是LURCACHE,另一个则是hashmap ,key就是我们传入的EngineKey,Value则是我们Resource的一个弱引用,我们先看下loadFromActiveResources方法:
@Nullable
private EngineResource<?> loadFromActiveResources(Key key) {
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
@Nullable
synchronized EngineResource<?> get(Key key) {
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null;
}
EngineResource<?> active = activeRef.get();
if (active == null) {
cleanupActiveReference(activeRef);
}
return active;
}
void cleanupActiveReference(@NonNull ResourceWeakReference ref) {
synchronized (this) {
activeEngineResources.remove(ref.key);
if (!ref.isCacheable || ref.resource == null) {
return;
}
}
EngineResource<?> newResource =
new EngineResource<>(
ref.resource, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ false, ref.key, listener);
listener.onResourceReleased(ref.key, newResource);
}
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource, /*forceNextFrame=*/ false);
}
}
synchronized void acquire() {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
++acquired;
}
loadFromActiveResources方法调用了getkey方法,实际上是从activeEngineResources 的hashmap中去取并返回一个Resource弱引用,通过这个弱引用去拿到真正缓存的Resource,如果缓存为空,则调用cleanupActiveReference方法,第一行activeEngineResources.remove(ref.key)表示,如果弱引用为空表示被回收了,那么这个key也将直接移除。下面判断如果ref.resource没有被回收,那么再根据这个resource 创建一个新的Resource对象并调用onResourceReleased方法。继续看这个方法,可以看出把它放进了cache缓存,这里的cache缓存就是我们上面说的LRUCACHE内存缓存。
继续回到loadFromActiveResources,然后调用acquire方法,看这个方法做了一个++的操作,实际上是对当前缓存做了引用计数+1,当我们每次拿到了缓存,引用计数就加一,如果销毁了,引用计数就减一,当为0的时候,我们的HashMap则会去回收这个缓存。到这里loadFromActiveResources就结束了,下面我们看下loadFromCache方法:
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource<?>) cached;
} else {
result =
new EngineResource<>(
cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
}
return result;
}
synchronized void activate(Key key, EngineResource<?> resource) {
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
第一行调用了getEngineResourceFromCache,然后直接调用了cache.remove方法,cache是一个MemoryCache接口,实现类是LruResourceCache,这里直接将获取到的缓存移除了,回到loadFromCache方法,如果缓存不为空,同样调用的引用计数方法,然后调用了activeResources.activate(key, cached)方法,这里直接创建了一个弱引用,然后添加到了弱引用的hashmap中。
内存缓存的引用计数是如何运作的?
前面几个章节只是简述了存储和获取内存缓存,没有仔细分析引用计数和什么时候触发,首先我们回到获取内存缓存的位置看看,比较下弱引用缓存和内存缓存的区别在哪:
Engine 类
@Nullable
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
这里activeResources.get(key)直接从弱引用缓存中获取,那么我们看这个activeResources是存在与当前的Engine 中的,要知道我们看active.acquire()方法源码:
EngineResource 类
synchronized void acquire() {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
++acquired;
}
void release() {
synchronized (listener) {
synchronized (this) {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (--acquired == 0) {
listener.onResourceReleased(key, this);
}
}
}
}
@Override
public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
我们看到这里的acquired做了一次++,表示内存中引用了当前的缓存图片 ,那么相反的,什么时候调用--呢,我们发现只有release方法做了--操作,当acquired=0的时候调用了onResourceReleased方法,我们跟进去看这个是干嘛的。发现当acquired=0的时候,activeResources做了清空key和resource的操作,然后调用cache.put(cacheKey, resource)方法转移存储到了lrucache内存缓存。那么我们看看这个release都在什么时候才回去回收,调用回收的地方很多,其中有这样的一块:
SingleRequest 类
public synchronized void clear() {
assertNotCallingCallbacks();
stateVerifier.throwIfRecycled();
if (status == Status.CLEARED) {
return;
}
cancel();
if (resource != null) {
releaseResource(resource);
}
if (canNotifyCleared()) {
target.onLoadCleared(getPlaceholderDrawable());
}
status = Status.CLEARED;
}
RequestTracker 类
private boolean clearRemoveAndMaybeRecycle(@Nullable Request request, boolean isSafeToRecycle) {
if (request == null) {
return true;
}
boolean isOwnedByUs = requests.remove(request);
isOwnedByUs = pendingRequests.remove(request) || isOwnedByUs;
if (isOwnedByUs) {
request.clear();
if (isSafeToRecycle) {
request.recycle();
}
}
return isOwnedByUs;
}
RequestManager 类
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
看到RequestManager的onDestroy方法我想就明白了,引用计数和弱引用缓存activeResource是跟activity绑定在一起的,当activity销毁的时候,那么同时也会回收activeResource的资源并移动到cache内存缓存,此时引用计数为0,当从cache内存缓存中取数据的时候,取完则存储到activeResource缓存,引用计数又++ 。所以这里的引用技术表示当前使用的是activeResource缓存,当activeResource缓存即将被回收的时候,转移到Cache缓存,引用计数--。
总结
看到这里有头绪了吗,为什么要设置两道缓存,为什么获取到的缓存要互相移动,其实以常规的思维,这里只需用LRUCACHE即可,但是这样的设计,避免了频繁去读取LURCACHE并且如果存储的图片资源过多,lrucache也会回收之前的缓存,所以为了避免被回收掉,当我们从lrucache拿到缓存后立马移动到hashmap的弱引用缓存。当然弱引用缓存也有可能被系统回收,所以当弱引用资源还未被回收的时候,再移动到LRUCACHE缓存中,两个缓存互相利用和结合,最大效率的使用了图片资源。到这里我们的内存缓存读取就分析结束了,下一节我们重点分析请求的处理和磁盘缓存。
网友评论