我們最常用的gilde加載圖片的時候就用短短的方法就將圖片展示出來了:
Glide.with(this).load(url).into(imageview);
在這簡單的操作后面是很多的代碼,先從這一個個方法開始看起具则。
1.with()
RequestManager with()
public class Glide{
·····
//適用于在正常fragment之外使用的資源或activity生命周期(在服務中或者通知縮略圖)
//@param context 任何上下文都不會被保留
//@return 可用于啟動加載的頂級應用程序的RequestManager
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
//該加載與傳入的activity生命周期相關聯(lián)
//
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
}
- with()方法重載的中類很多即纲,可以傳入context、activity博肋、fragment等
- RequsetManager調用get方法獲得RequestManagerRetriever對象
- RequestManagerRetriever調用get方法獲取RequestManager對象
Glide#getRetriever
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
調用Glide.get方法低斋,初始化glide
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
Glide加載是個靜態(tài)方法,且采用單例模式
RequestManagerRetriever#get()
public class RequestManagerRetriever implements Handler.Callback {
```
private volatile RequestManager applicationManager;
@NonNull
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or
// activity. However, in this case since the manager attached to the application will not
// receive lifecycle events, we must force the manager to start resumed using
// ApplicationLifecycle.
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper
// Only unwrap a ContextWrapper if the baseContext has a non-null application context.
// Context#createPackageContext may return a Context without an Application instance,
// in which case a ContextWrapper may be used to attach one.
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(
fragment.getContext(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getContext().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
······
}
這里的get方法是根據(jù)傳入的不同的參數(shù)制定的束昵。實際上只分為兩種參數(shù)Application類型的參數(shù)和傳入非Application類型的參數(shù)拔稳。
- 傳入Application參數(shù)類型的情況:
在Glide.with()方法中傳入的是一個Application對象,就會調用get()方法中參數(shù)為context的方法锹雏。然后會調用getApplicationManager()方法來獲取一個RequestManager對象巴比。
這是最簡單的一種情況。因為Application對象的生命周期就是應用程序的生命周期。Glide的生命周期和應用程序的生命周期就是同步的轻绞。關閉應用程序采记,Glide的加載也會終止。
- 傳入非Application參數(shù)類型的情況:
這種情況下會像當前傳入的Activity等中添加一個隱藏的fragment政勃。比如說Glide正在加載一張圖片如果此時關閉了當前應用唧龄,那么glide應當停止對當前圖片的加載,但是glide并不知道當前activity的生命周期奸远,但是fragment的生命周期和activity是同步的既棺,若activity被銷毀了,fragment是可以監(jiān)聽到的懒叛,因此添加一個隱藏的fragment丸冕。
RequestManagerRetriever#fragmentGet
添加隱藏的fragment在supportFragmentGet()和fragmentGet()方法中。這兩個方法中有一個類RequestManagerFragment繼承于fragment薛窥,創(chuàng)建了fragment對象胖烛。
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
//創(chuàng)建fragment對象
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
RequestManagerFragment
public class RequestManagerFragment extends Fragment {
······
public RequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
······
}
這里面定義了一個ActivityFragmentLifecycle類來監(jiān)聽fragment生命周期的變化
ActivityFragmentLifecycle
LifecycleListener監(jiān)聽fragment生命周期
class ActivityFragmentLifecycle implements Lifecycle {
@Override
public void addListener(@NonNull LifecycleListener listener) {
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
@Override
public void removeListener(@NonNull 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();
}
}
}
2. load()
RequestManager
因為with()方法返回的是RequestManager對象,所以調用load方法的類肯定是RequestManager诅迷。load方法可加載的類型有很多佩番,這里只拿加載圖片的url為例。
我看的這一版的gilde已經(jīng)是3.10.0的了罢杉。網(wǎng)上一些博客中的DrawTypeRequest已經(jīng)沒有了趟畏。
public class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes<RequestBuilder<Drawable>>{
······
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
public RequestBuilder<Drawable> load(@Nullable URL url) {
return asDrawable().load(url);
}
//as方法中傳入的是Drawable對象
@NonNull
@CheckResult
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class);
}
//resourceClass要解碼的資源
//該方法返回一個新的請求構建器,用于加載給定的資源類屑那,進入RequestBuilder
@NonNull
@CheckResult
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
······
}
看到加載圖片的load方法都返回RequestBuilder<Drawable>對象拱镐,且load方法都先由asDrawable()調用。
上面的源碼是從load()-->asDrawable()-->as()的過程持际,as方法中的resourceClass就是傳進來的Drawable.class這里的drawable指代的是一種資源的類型。下面再看下RequestBuilder這個類哗咆。
RequestManager中的load方法主要就是通過實例化RequestBuilder蜘欲,再調用RequestBuilder中的load方法。
RequestBuilder
public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
//構造方法晌柬,as中調用的方法就是這個
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();
initRequestListeners(requestManager.getDefaultRequestListeners());
apply(requestManager.getDefaultRequestOptions());
}
······
//load加載的資源類型有很多種姥份,這里還是只放了url的
public RequestBuilder<TranscodeType> load(@Nullable Uri uri) {
return loadGeneric(uri);
}
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
}
在這個類可以看見一個自定義的泛型,TranscodeType轉碼類型年碘,他代表可以傳進來的url file bitmp等不同類型的資源澈歉。官方介紹是一個通用類,可以處理通用資源類型的設置選項和啟動負載屿衅。
load方法又調用了loadGeneric方法埃难,這里面設置了兩個變量,mode和isModleSet。
into
從load.into的調用關系可以知道into方法在RequestBuilder類中
@NonNull
public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
@NonNull
@Synthetic
<Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
Executor callbackExecutor) {
return into(target, targetListener, /*options=*/ this, callbackExecutor);
}
@NonNull
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方法最終會返回一個target對象涡尘。通過glide.buildImageViewTarget(view,transcodeClass)創(chuàng)建一個target對象忍弛,Target對象是最終用來展示圖片的。
buildImageViewTarget
@NonNull
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)");
}
}
創(chuàng)建target對象主要分兩種:
- 加載圖片時調用了asBitmap()方法考抄,會構建BitmapImageViewTarget對象
- 其他情況细疚,構建GlideDrawableImageviewTarget對象
GenericRequestBuilder#into(Target target)
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加載資源的請求類 Target繼承于LifecyclerListener,通過getRequest檢索到此目標當前的請求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
//跟蹤、取消和重新啟動進程中川梅、已完成和失敗請求
requestManager.track(target, request);
return target;
//3.7.0版本
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
//執(zhí)行圖片請求疯兼,根據(jù)是不是加載狀態(tài)執(zhí)行
requestTracker.runRequest(request);
return target;
}
這里會檢查是不是在主線程,因為更新ui的操作必須在主線程贫途。這里通過target對象獲取request對象镇防,然后清除之前的request對象,回收request對象潮饱,然后重新構建一個request對象来氧,并給這個target對象設置request對象。
下面的分析拿glide3.7.0的版本來看∠憷現(xiàn)在4.10.0版本和之前的版本差別太大了啦扬。
在這之后就開始加載圖片。buildRequest()方法構建出來Request對象凫碌,buildRequest-->buildRequestRecursive()-->obtainRequest-->GenericRequest.obtain()-->REQUEST_POOL.poll()扑毡。調用了請求池的poll方法,獲取request對象盛险。如果這個對象為空就創(chuàng)建一個GenericRequest對象瞄摊,然后用request對象的init方法進行初始化。
runRequest
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
//執(zhí)行request
request.begin();
} else {
//將request加入待執(zhí)行隊列
pendingRequests.add(request);
}
}
先判斷Glide當前是不是處于暫停狀態(tài)苦掘。
在執(zhí)行圖片的加載過程中有幾個重要的類:
- ModeLoader:獲取原始數(shù)據(jù)的類
- ResourceTranscoder:圖片資源轉換的類
- Engine:負責啟動負載并管理活動和緩存的資源類
- DataLoadProvider:負責圖片的編碼和解碼
- DataFetcher:將流資源轉換為glide實際加載圖片需要的數(shù)據(jù)换帜,比如byte[] file uri url等
- EngineJob:開啟線程,為異步加載圖片做準備
緩存
glide緩存主要分為內(nèi)存緩存和硬盤緩存鹤啡。這里將從這兩個方向來分析惯驼。
緩存key
緩存功能要實現(xiàn)的話肯定是要有進行緩存的key的。拿到這個key我們才能找到對應的圖片递瑰。
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
...
}
...
}
這里調用fetcher.getId()方法獲得了一個id字符串祟牲,這個字符串是加載圖片的唯一標識。EngineKey就是保存key的類抖部,一個key有10個標識可以區(qū)分说贝。
內(nèi)存緩存
默認情況下,Glide自動就是會開啟內(nèi)存緩存的慎颗。如果不想開啟內(nèi)存緩存乡恕,只需要設置skipMemoryCache(true)就可以了言询。
Glide.with(this)
.load(url)
.skipMemoryCache(true)
.into(imageView);
Glide緩存實現(xiàn)了LruCache算法,還結合了一種弱引用的機制几颜,共同完成了內(nèi)存緩存的功能倍试。
Glide獲取緩存資源對象
loadGeneric()-->Glide.buildStreamModelLoader()-->buildModelLoader()-->Glide.get()-->createGlide()
public class GlideBuilder {
...
Glide createGlide() {
if (sourceService == null) {
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
sourceService = new FifoPriorityThreadPoolExecutor(cores);
}
if (diskCacheService == null) {
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
}
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
if (bitmapPool == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int size = calculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (memoryCache == null) {
//Glide實現(xiàn)內(nèi)存緩存使用的LruCache對象
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}
if (decodeFormat == null) {
decodeFormat = DecodeFormat.DEFAULT;
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
}
LruCache算法的實現(xiàn)主要的點就在于它用了LindedHashMap來保存數(shù)據(jù),其中的一個特性accessOrder = true是基于訪問順序的蛋哭,再加上對LindkedHashMap的數(shù)據(jù)操作上鎖實現(xiàn)的緩存策略县习。調用get方法訪問緩存對象時,就會調用LinkedHashMap的get()方法獲取對應集合的元素谆趾,同時會更新該元素到隊列躁愿。當調用put()方法時,就會在集合中添加元素沪蓬,并調用trimToSize()判斷緩存是否以滿彤钟,如果滿了就用LinkedHashMap的迭代器刪除對首元素,也就是近期最少訪問的元素跷叉。
Engine#load()
內(nèi)存緩存的代碼在這里實現(xiàn)
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
...
}
在這個方法里其實可以把加載圖片的過程分為三步:
- loadFromCache():從緩存中獲取
- loadFromActivityResources():獲取緩存圖片
- engineJob.start(runnable):開啟線程從網(wǎng)絡加載圖片
Glide加載過程中會調用loadFromCache()和loadFromActivityResources()這兩個方法來獲取內(nèi)存緩存逸雹。一個使用的是LruCache算法,另一個使用的是弱引用云挟。
loadFromCache/loadFromActivityResources
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
private final MemoryCache cache;
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
...
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
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) {
result = (EngineResource) cached;
} else {
result = new EngineResource(cached, true /*isCacheable*/);
}
return result;
}
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;
}
...
}
loadFromCache()方法先會判斷是否開啟了內(nèi)存緩存梆砸,開啟了則會調用getEngineResourceFromCache()方法從cache對象也就是LruResourceCache中獲取圖片資源,但是卻把這個資源從緩存中removele园欣。獲取到這個資源后把這個資源又加入到了activeResources中帖世。這很令我困惑,為什么這樣做沸枯,按理來說Lru會根據(jù)獲取到的這個資源會把它重新加入到隊列后面日矫,為了就是會被刪除。
activeResources就是一個弱引用的HashMap,用來緩存正在使用中的圖片绑榴。loadFromActiveResources()方法就是從activeResources這個HashMap當中取值的哪轿。使用activeResources來緩存正在使用中的圖片可以保護這些圖片不會被LruCache算法回收掉。
內(nèi)存緩存的寫入
當圖片加載完成后彭沼,會在EngineJob當中通過Handler發(fā)送一條消息將執(zhí)行邏輯切回到主線程當中缔逛,從而執(zhí)行了handleResultOnMainThread()方法。
class EngineJob implements EngineRunnable.EngineRunnableManager {
private final EngineResourceFactory engineResourceFactory;
...
private void handleResultOnMainThread() {
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
engineResource.release();
}
static class EngineResourceFactory {
public <R> EngineResource<R> build(Resource<R> resource, boolean isMemoryCacheable) {
return new EngineResource<R>(resource, isMemoryCacheable);
}
}
...
}
EngineJob這個類就是主要就是來執(zhí)行添加(addCallBack)和刪除(rmoveCallBack)回調姓惑,并在回調時通知回調來管理負載的類加載完成。
handleResultOnMainThread()方法中通過EngineResourceFactory構建出了一個包含圖片資源的EngineResource對象按脚,將這個對象傳入Engine的onEngineJobComplete方法中于毙。
onEngineJobComplete
@Override
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
Util.assertMainThread();
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null) {
resource.setResourceListener(key, this);
if (resource.isCacheable()) {
activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
}
}
// TODO: should this check that the engine job is still current?
jobs.remove(key);
}
可看到回調過來的EngineResource被put到了activeResources當中,在這里將圖片寫入了緩存辅搬。但這里只是弱引用緩存唯沮,還有一種LruCache在哪里呢脖旱?
acquire()和release()
class EngineResource<Z> implements Resource<Z> {
private int acquired;
...
void acquire() {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
if (!Looper.getMainLooper().equals(Looper.myLooper())) {
throw new IllegalThreadStateException("Must call acquire on the main thread");
}
++acquired;
}
void release() {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (!Looper.getMainLooper().equals(Looper.myLooper())) {
throw new IllegalThreadStateException("Must call release on the main thread");
}
if (--acquired == 0) {
listener.onResourceReleased(key, this);
}
}
}
這兩個方法handleResultOnMainThread()方法中出現(xiàn)過,engineResource調用了acquire()方法后介蛉,才執(zhí)行了onEngineJobComplete()方法中萌庆,也就是將圖片加入了activeResources弱引用集合中。將圖片加入弱引用map達到將當前正在使用的圖片加入的效果币旧,就出現(xiàn)了acquire和release方法践险。
當acquired變量大于0時,說明圖片正在使用吹菱,應該放入activeResources弱引用緩存當中巍虫。而經(jīng)過release()之后,如果acquired值為0了鳍刷,說明圖片不再被使用了占遥,將會調用listener的onResourceReleased()方法來釋放資源。
onResourceReleased()
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
private final MemoryCache cache;
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
...
@Override
public void onResourceReleased(Key cacheKey, EngineResource resource) {
Util.assertMainThread();
activeResources.remove(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
...
}
這里首先會將緩存圖片從activeresources中移除输瓜,然后再將它put到LruResourceCache中瓦胎。否則就進行資源回收。這樣就實現(xiàn)了正在使用中的圖片使用弱引用來進行緩存尤揣,不在使用中的圖片使用LruCache來進行緩存搔啊。
硬盤緩存
禁止硬盤緩存可以這樣使用:
Glide.with(this)
.load(url)
.disCacheStrategy(DiskCacheStrategy.NONE)
.into(imageview);
disCacheStrategy可以接收四種參數(shù):
- DiskCacehStrategy.NONE:不緩存任何內(nèi)容
- DiskCacehStrategy.SOURCE:只緩存原始圖片
- DiskCacehStrategy.RESULT:只緩存轉換后的圖片
- DiskCacehStrategy.ALL:既緩存原始圖片,也緩存轉換后的圖片
decode()
Glide開啟線程加載圖片后會執(zhí)行EngineRunnable的run()方法芹缔,run方法中又會調用一個decode方法
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
這里有兩個方法:
- decodeFromCache():從硬盤緩存中讀取圖片
- decodeFromSource():讀取原始圖片
Glide會優(yōu)先從硬盤緩存中讀取
decodeFromCache
private Resource<?> decodeFromCache() throws Exception {
Resource<?> result = null;
try {
//DisCacheStrategy.RESULT
result = decodeJob.decodeResultFromCache();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Exception decoding result from cache: " + e);
}
}
if (result == null) {
//DisCacheStrategy.SOURCE
result = decodeJob.decodeSourceFromCache();
}
return result;
}
執(zhí)行兩個方法的區(qū)別就在于傳入disCacheStrategy方法中的參數(shù)
decodeResultFromCache
public Resource<Z> decodeResultFromCache() throws Exception {
if (!diskCacheStrategy.cacheResult()) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<T> transformed = loadFromCache(resultKey);
startTime = LogTime.getLogTime();
Resource<Z> result = transcode(transformed);
return result;
}
先調用loadFromCache()從緩存中讀取數(shù)據(jù)坯癣,直接將數(shù)據(jù)解碼并返回
decodeSourceFromCache()
public Resource<Z> decodeSourceFromCache() throws Exception {
if (!diskCacheStrategy.cacheSource()) {
return null;
}
long startTime = LogTime.getLogTime();
Resource<T> decoded = loadFromCache(resultKey.getOriginalKey());
return transformEncodeAndTranscode(decoded);
}
先調用loadFromCache()從緩存中讀取數(shù)據(jù),調用transformEncodeAndTrancode()方法先將數(shù)據(jù)轉換再解碼返回最欠。
這兩個方法在調用loadFromCache()方法中傳入的參數(shù)不同示罗。因為Glide緩存的key是由10個參數(shù)共同組成的。如果是緩存原始圖片芝硬,不需要那么多的參數(shù)蚜点。
硬盤緩存的讀取
private Resource<T> loadFromCache(Key key) throws IOException {
File cacheFile = diskCacheProvider.getDiskCache().get(key);
if (cacheFile == null) {
return null;
}
Resource<T> result = null;
try {
result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
} finally {
if (result == null) {
diskCacheProvider.getDiskCache().delete(key);
}
}
return result;
}
硬盤緩存的讀取在loadFromCache()中。getDishCache()方法獲取到Glide自己編寫的DiskLruCache工具類的實例拌阴,然后調用它的get()方法把緩存key傳入绍绘,就獲得硬盤緩存的文件了。文件不為空迟赃,就將其解碼成Resource對象后返回就行陪拘。
decodeFromSource()
public Resource<Z> decodeFromSource() throws Exception {
//解析原圖片
Resource<T> decoded = decodeSource();
//對圖片進行轉換和轉碼,再將轉換過后的圖片寫入到硬盤緩存中
return transformEncodeAndTranscode(decoded);
}
在沒有緩存的情況下,會調用decodeFromDecode()方法來讀取原始圖片纤壁。
decodeSource()
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();
//當相應的資源不在緩存中時才調用此方法左刽,異步獲取可以從中解碼資源的數(shù)據(jù)
//fetcher是ImageVideoFetcher HttpUrlFetcher中真正進行網(wǎng)絡請求
final A data = fetcher.loadData(priority);
if (isCancelled) {
return null;
}
//對數(shù)據(jù)進行解碼
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
decodeFromSourceData
private Resource<T> decodeFromSourceData(A data) throws IOException {
final Resource<T> decoded;
//是否允許緩存原始圖片
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
}
return decoded;
}
private Resource<T> cacheAndDecodeSourceData(A data) throws IOException {
long startTime = LogTime.getLogTime();
SourceWriter<A> writer = new SourceWriter<A>(loadProvider.getSourceEncoder(), data);
diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
startTime = LogTime.getLogTime();
Resource<T> result = loadFromCache(resultKey.getOriginalKey());
return result;
}
調用getDiskCache()方法來獲取DiskLruCache實例,調用put方法寫入硬盤緩存酌媒。原始圖片的緩存key是用的resultKey.getOriginalKey()