上一節(jié)我們解析了為什么Glide能監(jiān)聽頁面的生命周期而保證及時(shí)回收請求和其他資源砰诵,有需要的話看下上一節(jié):
上節(jié)我們講到了Glide.with(this).load(url).into(imageView)前面兩個(gè)方法龙助,這一節(jié)我們看下最重要的一環(huán)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方法的返回值是一個(gè)ViewTarget,ViewTarget是對imageview的一個(gè)封裝样悟,我們后面會講到尚卫,這里第一二行則是判斷是否在主線程和做的一個(gè)非空判斷遏匆,我們看后面的代碼,這里的if語句主要是對ScaleType進(jìn)行設(shè)置码泞,獲取到requestOptions并且設(shè)置ScaleType兄旬,我們看下下面的return語句的into方法,第三個(gè)字段看得出來傳入了一個(gè)線程池余寥,是不是覺得圖片請求快開始了呢领铐,我們先看下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方法中宋舷,而這是一個(gè)工廠方法绪撵,針對不同的加載類型生成不同的target,target這里已經(jīng)與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方法音诈,看命名方式可以猜出,這里應(yīng)該是創(chuàng)建一個(gè)請求了绎狭,這里的代碼比較長细溅,我做了一個(gè)簡化:
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");
}
}
前面的省略號表示不重要的信息,最終調(diào)用到的是init的方法儡嘶,這里為請求request初始化了很多參數(shù)谒兄,例如我們前面設(shè)置過的model,就是請求的URL社付,還有是否重新設(shè)定了加載的寬高,優(yōu)先級邻耕,動畫等等設(shè)置鸥咖,這里就是對請求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獲取了一個(gè)previous 的request啼辣,隨后調(diào)用了requestManager.clear(target),這里是保證每次請求都是最新的參數(shù)和設(shè)置御滩,清除上一個(gè)請求鸥拧。這里的 target.setRequest(request);我們點(diǎn)進(jìn)去發(fā)現(xiàn)調(diào)用的是setTag方法,將view和request綁定削解。最后調(diào)用了requestManager.track(target, request)富弦,還記得前面我們?yōu)槭裁凑fRequestManager管理target和request么,我們進(jìn)track方法看下代碼:
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
這里還使用的是同步方法氛驮,這里持有了兩個(gè)追蹤器腕柜,一個(gè)是target,一個(gè)是request,進(jìn)track方法看里面的內(nèi)容:
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();
}
}
我們發(fā)現(xiàn)targetTracker維護(hù)了一個(gè)set集合盏缤,用于存儲和管理所有的target對象砰蠢,下面的幾個(gè)方法,也正是同步于fragment的和requestmanager的生命周期方法唉铜,便于在出發(fā)生命周期時(shí)對所有的target對象執(zhí)行操作台舱。我們再來看下requestTracker的runRequest方法,看方法命名是不是猜到快要執(zhí)行網(wǎng)絡(luò)請求了呢潭流,來看下代碼:
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同樣維護(hù)了一個(gè)requests Set集合管理所有請求竞惋。isPaused參數(shù)表示如果暫停,那么當(dāng)前的request添加到一個(gè)等待集合中延遲執(zhí)行幻枉,否則直接執(zhí)行begin方法碰声,我們繼續(xù)看下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));
}
}
}
這里調(diào)用的是SingleRequest的begin方法,前面是對當(dāng)前request運(yùn)行狀態(tài)的判斷熬甫,status = Status.WAITING_FOR_SIZE這里直接賦值胰挑,表示當(dāng)前需要等待獲取到view的寬高才能進(jìn)行下一步,如果你在前面的設(shè)置中設(shè)置過override方法傳遞了新的寬高椿肩,那么這里直接使用你傳入的寬高瞻颂,否則調(diào)用target.getSize(this)方法去獲取圖片所加載的view的寬高,我們跟進(jìn)去看這個(gè)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);
}
}
我們發(fā)現(xiàn)走到了ViewTarget的getSize方法郑象,ViewTarget正是當(dāng)初我們封裝imageview的target對象贡这,這里直接去獲取getTargetWidth、Height厂榛,我們不進(jìn)去看了盖矫,就是拿到我們之前傳遞的image view,獲取他的寬高击奶。這里的isViewStateAndSizeValid方法則是判斷獲取的寬高是否大于0 并且真實(shí)可用辈双,可用就表示當(dāng)前的image view其實(shí)已經(jīng)繪制完成,我們獲取的到它的寬高并且直接返回柜砾。如果不可用湃望,那么就給image view添加一個(gè)繪制的監(jiān)聽addOnPreDrawListener,當(dāng)繪制完成時(shí)我們再去獲取它的寬高并且返回痰驱。
我們再回到前面的target.getSize方法证芭,繼續(xù)看下去,寬高獲取完成后担映,調(diào)用的是onSizeReady废士,那么我們跟進(jìn)去看下:
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狀態(tài)了,下面一行的sizeMultiplier變量蝇完,還記得我們第一節(jié)的Glide的常規(guī)使用嗎湃密,它作用域縮略圖诅挑,表示我們想將原圖縮放幾倍大小,這里是獲取設(shè)置并且計(jì)算新的縮略圖寬高泛源,我們不去細(xì)究拔妥,看下面的重點(diǎn),engine.load方法达箍∶涣可以看出,這個(gè)方法傳遞了很多參數(shù)和設(shè)置缎玫,包括參數(shù)設(shè)置廣泛的requestOptions硬纤,這里應(yīng)該即將開始加載請求的信息了,我們繼續(xù)看下去:
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;
}
重點(diǎn)來了赃磨,看第一行的 keyFactory.buildKey方法筝家,返回值是一個(gè)EngineKey,這個(gè)key就是我們用于緩存的key邻辉,再看看入?yún)⒌膸讉€(gè)字段溪王。model,signature值骇,width莹菱,height,options等吱瘩。由此可見道伟,影響一張緩存的圖片,不只是他的url使碾,還包括了要加載的圖片寬高蜜徽、簽名和設(shè)置等,所以當(dāng)同一個(gè)圖片的URL票摇,如果我們加載不同的寬高拘鞋,那么這個(gè)URL是會緩存兩次的。
我們繼續(xù)看有一個(gè)memoryResource對象兄朋,并且調(diào)用了loadFromMemory方法,我們跟進(jìn)去看一下:
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;
}
第一行我們看到了一個(gè)isMemoryCacheable,這個(gè)就是我們設(shè)置的skipMemoryCache响鹃,如果沒有設(shè)置案训,那么默認(rèn)就是可用的强霎,這里如果設(shè)置了跳過內(nèi)存緩存,那么直接執(zhí)行return語句轩触。繼續(xù)看這里有兩個(gè)loadFrom方法拉馋,一個(gè)是loadFromActiveResources,另一個(gè)是loadFromCache景馁,那么兩個(gè)有什么區(qū)別呢板壮?其實(shí)兩個(gè)都是作為內(nèi)存緩存存在的透葛,一個(gè)內(nèi)部維護(hù)的是LURCACHE萨蚕,另一個(gè)則是hashmap 奕翔,key就是我們傳入的EngineKey庆猫,Value則是我們Resource的一個(gè)弱引用纫普,我們先看下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方法調(diào)用了getkey方法,實(shí)際上是從activeEngineResources 的hashmap中去取并返回一個(gè)Resource弱引用烤芦,通過這個(gè)弱引用去拿到真正緩存的Resource举娩,如果緩存為空,則調(diào)用cleanupActiveReference方法构罗,第一行activeEngineResources.remove(ref.key)表示铜涉,如果弱引用為空表示被回收了,那么這個(gè)key也將直接移除遂唧。下面判斷如果ref.resource沒有被回收芙代,那么再根據(jù)這個(gè)resource 創(chuàng)建一個(gè)新的Resource對象并調(diào)用onResourceReleased方法。繼續(xù)看這個(gè)方法盖彭,可以看出把它放進(jìn)了cache緩存纹烹,這里的cache緩存就是我們上面說的LRUCACHE內(nèi)存緩存。
繼續(xù)回到loadFromActiveResources召边,然后調(diào)用acquire方法铺呵,看這個(gè)方法做了一個(gè)++的操作,實(shí)際上是對當(dāng)前緩存做了引用計(jì)數(shù)+1掌实,當(dāng)我們每次拿到了緩存陪蜻,引用計(jì)數(shù)就加一邦马,如果銷毀了贱鼻,引用計(jì)數(shù)就減一宴卖,當(dāng)為0的時(shí)候,我們的HashMap則會去回收這個(gè)緩存邻悬。到這里loadFromActiveResources就結(jié)束了症昏,下面我們看下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();
}
}
第一行調(diào)用了getEngineResourceFromCache,然后直接調(diào)用了cache.remove方法父丰,cache是一個(gè)MemoryCache接口肝谭,實(shí)現(xiàn)類是LruResourceCache,這里直接將獲取到的緩存移除了蛾扇,回到loadFromCache方法攘烛,如果緩存不為空,同樣調(diào)用的引用計(jì)數(shù)方法镀首,然后調(diào)用了activeResources.activate(key, cached)方法坟漱,這里直接創(chuàng)建了一個(gè)弱引用,然后添加到了弱引用的hashmap中更哄。
內(nèi)存緩存的引用計(jì)數(shù)是如何運(yùn)作的芋齿?
前面幾個(gè)章節(jié)只是簡述了存儲和獲取內(nèi)存緩存,沒有仔細(xì)分析引用計(jì)數(shù)和什么時(shí)候觸發(fā)成翩,首先我們回到獲取內(nèi)存緩存的位置看看觅捆,比較下弱引用緩存和內(nèi)存緩存的區(qū)別在哪:
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)直接從弱引用緩存中獲取,那么我們看這個(gè)activeResources是存在與當(dāng)前的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做了一次++栅炒,表示內(nèi)存中引用了當(dāng)前的緩存圖片 ,那么相反的庸论,什么時(shí)候調(diào)用--呢职辅,我們發(fā)現(xiàn)只有release方法做了--操作,當(dāng)acquired=0的時(shí)候調(diào)用了onResourceReleased方法聂示,我們跟進(jìn)去看這個(gè)是干嘛的域携。發(fā)現(xiàn)當(dāng)acquired=0的時(shí)候,activeResources做了清空key和resource的操作鱼喉,然后調(diào)用cache.put(cacheKey, resource)方法轉(zhuǎn)移存儲到了lrucache內(nèi)存緩存秀鞭。那么我們看看這個(gè)release都在什么時(shí)候才回去回收,調(diào)用回收的地方很多扛禽,其中有這樣的一塊:
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方法我想就明白了锋边,引用計(jì)數(shù)和弱引用緩存activeResource是跟activity綁定在一起的,當(dāng)activity銷毀的時(shí)候编曼,那么同時(shí)也會回收activeResource的資源并移動到cache內(nèi)存緩存豆巨,此時(shí)引用計(jì)數(shù)為0,當(dāng)從cache內(nèi)存緩存中取數(shù)據(jù)的時(shí)候掐场,取完則存儲到activeResource緩存往扔,引用計(jì)數(shù)又++ 贩猎。所以這里的引用技術(shù)表示當(dāng)前使用的是activeResource緩存,當(dāng)activeResource緩存即將被回收的時(shí)候萍膛,轉(zhuǎn)移到Cache緩存吭服,引用計(jì)數(shù)--。
總結(jié)
看到這里有頭緒了嗎蝗罗,為什么要設(shè)置兩道緩存艇棕,為什么獲取到的緩存要互相移動,其實(shí)以常規(guī)的思維串塑,這里只需用LRUCACHE即可沼琉,但是這樣的設(shè)計(jì),避免了頻繁去讀取LURCACHE并且如果存儲的圖片資源過多桩匪,lrucache也會回收之前的緩存刺桃,所以為了避免被回收掉,當(dāng)我們從lrucache拿到緩存后立馬移動到hashmap的弱引用緩存吸祟。當(dāng)然弱引用緩存也有可能被系統(tǒng)回收瑟慈,所以當(dāng)弱引用資源還未被回收的時(shí)候,再移動到LRUCACHE緩存中屋匕,兩個(gè)緩存互相利用和結(jié)合葛碧,最大效率的使用了圖片資源。到這里我們的內(nèi)存緩存讀取就分析結(jié)束了过吻,下一節(jié)我們重點(diǎn)分析請求的處理和磁盤緩存进泼。