我們先來看看glide最基本使用方式,如下:
Glide.with(this)
.asDrawable()
.load("http://i6.topit.me/6/5d/45/1131907198420455d6o.jpg")
.apply(fitCenterTransform(this))
.apply(placeholderOf(R.drawable.skyblue_logo_wechatfavorite_checked))
.into(imageView);
本片文章就從這個最簡單的使用方法來對源碼進行一波解析。
一、大概介紹
Glide.java 入口文件
這裏其實就是一個入口文件,所有功能都是放在後面的類中,這裏所有需要的組件都在這個文件裏面進行統一初始化。
RequestManager.java
這個類主要是用於管理和啓動glide的所有請求,可以使用activity,fragment或者連接生命週期的事件去只能的停止,啓動和重啓請求。也可以檢索或者通過實例化一個新的對象,或者使用靜態的glide去利用構建在activity和fragment生命週期處理中。它的方法跟你的fragment和activity是同步的。
RequestBuilder.java
可以處理設置選項,並啓動負載的通用資源類型。
看前面的例子代碼中,asDrawable()最終調用的的RequestBuilder中的transition()函數,這個方法主要是用於加載對象從佔位符(placeholder)或者縮略圖(thumbnail)到真正對象加載完成的專場動畫。
load()方法中,這裏可以加載很多類型的數據對象,可以是string,uri,file,resourceId,byte[]這些。對應的編碼方式也是不一樣的。
into()方法,是真正啓動加載的地方。
二、具體源碼解析
Glide.with()函數
先看一下glide中的with函數,如下:
public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
這裏返回的是RequestManagerRetriever.get(),我們看一下這個函數,如下:
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, null);
}
}
(當然這個with()可以傳很多類型進來,我們只看activity的情況)
源碼中,如果該activity在後臺的時候,我們進入get(Application)中,繼續我們剛剛說的這個步驟來。
所以我們就直接看fragmentGet(activity, fm, null)函數,源碼如下:
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm,
android.app.Fragment parentHint) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
new RequestManager(glide, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
這個函數作用正如它的名字一樣,就是獲取fragment,其實是返回的RequestManager,那這個到底是啥?看一下getRequestManagerFragment函數,源碼如下:
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(
final android.app.FragmentManager fm, android.app.Fragment parentHint) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
(其中傳進來的參數fm,是我們獲取的activity的FragmentManager)
這裏其實就是就是將我們傳進來的activity轉化爲glide需要的RequestManagerFragment,RequestManagerFragment是一個自己重新以的fragment而已。
asDrawable函數
源碼如下:
public RequestBuilder<Drawable> asDrawable() {
return as(Drawable.class).transition(new DrawableTransitionOptions());
}
添加一個DrawableTransitionOptions類型的動畫。
load函數
public RequestBuilder<Drawable> load(@Nullable Object model) {
return asDrawable().load(model);
}
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
return loadGeneric(model);
}
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
將你要傳遞的uri,file等等傳進來,放到RequestBuilder中。
into函數
最終我們都要調用這個into了,源碼如下:
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
if (requestOptions.isLocked()) {
requestOptions = requestOptions.clone();
}
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions.optionalCenterCrop(context);
break;
case CENTER_INSIDE:
requestOptions.optionalCenterInside(context);
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions.optionalFitCenter(context);
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(context.buildImageViewTarget(view, transcodeClass));
}
這裏針對ImageView的填充方式做了篩選並對應設置到requestOptions上。最終的是通過ImageView和轉碼類型(transcodeClass)創建不通過的Target(例如Bitmap對應的BitmapImageViewTarget和Drawable對應的DrawableImageViewTarget)。
到這裏,我們看到的源碼,並沒有涉及到解碼、緩存、加載等這些功能啊,是不是我們漏掉一些重要的函數?沒錯,我們的確漏掉一個很重要的函數,在into函數中,有一行代碼如下:
requestManager.track(target, request);
這個就是最核心的方法,它纔是真正觸發請求、編解碼、裝載、緩存等這些功能的。下面我詳細介紹一下。
track()函數
void track(Target<?> target, Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
第一行,就是把target加入targets隊列(WeakHashMap)中。
看一下runRequest函數,如下:
public void runRequest(Request request) {
requests.add(request);//添加到內存緩存
if (!isPaused) {
request.begin();//開始
} else {
pendingRequests.add(request);//掛起請求
}
}
然後看一下request.begin()是如何運作的,SingleRequest繼承一個抽象類,定義了begin方法,所以我們看一下SingleRequest中begin方法。
@Override
public void begin() {
stateVerifier.throwIfRecycled();
startTime = LogTime.getLogTime();
// 如果model空的,那麼是不能執行的。 這裏的model就是前面提到的RequestBuilder中的model
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
// Only log at more verbose log levels if the user has set a fallback drawable, because
// fallback Drawables indicate the user expects null models occasionally.
int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
status = Status.WAITING_FOR_SIZE;
//如果當前的View尺寸獲取到了,就會進入加載流程
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
//如果等待和正在執行狀態,那麼當前會加載佔位符Drawable
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
看上面註釋,如果,當前view尺寸還沒有獲取到,我們要執行target.getSize(this)函數,我們看一下這個函數實現方法(ViewTarget實現的)。
void getSize(SizeReadyCallback cb) {
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
int paddingAdjustedWidth = currentWidth == WindowManager.LayoutParams.WRAP_CONTENT
? currentWidth
: currentWidth - ViewCompat.getPaddingStart(view) - ViewCompat.getPaddingEnd(view);
int paddingAdjustedHeight = currentHeight == LayoutParams.WRAP_CONTENT
? currentHeight
: currentHeight - view.getPaddingTop() - view.getPaddingBottom();
cb.onSizeReady(paddingAdjustedWidth, paddingAdjustedHeight);
} else {
// We want to notify callbacks in the order they were added and we only expect one or two
// callbacks to
// be added a time, so a List is a reasonable choice.
if (!cbs.contains(cb)) {
cbs.add(cb);
}
if (layoutListener == null) {
//這是尺寸大小的監聽器
final ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
}
}
這裏加了ViewTreeObserver用來監聽view尺寸大小,我們可以看看SizeDeterminerLayoutListener做了什麼。
private static class SizeDeterminerLayoutListener implements ViewTreeObserver
.OnPreDrawListener {
private final WeakReference<SizeDeterminer> sizeDeterminerRef;
public SizeDeterminerLayoutListener(SizeDeterminer sizeDeterminer) {
sizeDeterminerRef = new WeakReference<>(sizeDeterminer);
}
@Override
public boolean onPreDraw() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "OnGlobalLayoutListener called listener=" + this);
}
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
// 通知SizeDeterminer去重新檢查尺寸,並觸發後續操作。
sizeDeterminer.checkCurrentDimens();
}
return true;
}
}
看完這個getsize,我們再返回到上面begin中的onSizeReady(overrideWidth, overrideHeight)函數,這個纔是真正加載的函數。主要是engine發起的load操作,如下:
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
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 (Log.isLoggable(TAG, Log.VERBOSE)) {
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.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
this);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
那麼這個engine從哪裏來的呢?它在glide初始化的時候,就創建了。
if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor);
}
包括參數內存緩存和磁盤緩存。然後我們看一下load具體實現,這裏代碼比較多,所以我就挑了幾個重要的看了一下:
//給每次加載資源創建一個key,作爲唯一的標識。
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//通過key load緩存資源,這是一級內存緩存,是從內存緩存中直接拿出來的
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
//這裏是二級內存緩存,使用Map<Key, WeakReference<EngineResource<?>>>保存起來的
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
//根據key獲取緩存的任務
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<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
useUnlimitedSourceExecutorPool);
DecodeJob<R> decodeJob = decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
//放入線程池,執行
engineJob.start(decodeJob);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
還有一個問題,這個二級緩存,是誰放入二級內存中呢,我們看一下上面一級緩存的loadFromCache方法。
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;
}
這裏把帶有key的緩存存到activeResources中,二級緩存中loadFromActiveResources函數,也是從activeResources中拿到資源,如下:
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = null;
//從activeResources中拿到key的cache
WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
if (activeRef != null) {
active = activeRef.get();
if (active != null) {
active.acquire();
} else {
activeResources.remove(key);
}
}
return active;
}
除此之外呢?
1、一級內存緩存,glide中默認的就是我們常用的LruResourceCache。
2、我們說glide是加載imageview,我們從哪裏知道的呢,還記得使用的時候into()函數中我們傳入的是imageview,而我們源碼中的target就是我們傳進來的imageview。在track函數中
public void track(Target<?> target) {
targets.add(target);
}
我們將target也就是我們的imageview加入到緩存中去了。
3、爲何要兩級內存緩存(loadFromActiveResources)。從資料上看猜測可能是一級緩存採用LRU算法進行緩存,不一定每個都能緩存到,添加二級緩存 可以保證每個都能緩存到;
4、EngineJob和DecodeJob各自職責:EngineJob充當了管理和調度者,主要負責加載和各類回調通知;DecodeJob是真正幹活的勞動者,這個類實現了Runnable接口。
那我們來看一下真正線程(DecodeJob)裏面是怎麼執行的?
我怕大家忘了engine.load的代碼,所以我先把使用EngineJob和DecodeJob的代碼貼在下面:
//創建任務
EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable,
useUnlimitedSourceExecutorPool);
DecodeJob<R> decodeJob = decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
//放入線程池,執行
engineJob.start(decodeJob);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
然後我們在繼續看一下DecodeJob是如何執行的:
@Override
public void run() {
// This should be much more fine grained, but since Java's thread pool implementation silently
// swallows all otherwise fatal exceptions, this will at least make it obvious to developers
// that something is failing.
try {
if (isCancelled) {
notifyFailed();
return;
}
runWrapped();
} catch (RuntimeException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "DecodeJob threw unexpectedly"
+ ", isCancelled: " + isCancelled
+ ", stage: " + stage, e);
}
// When we're encoding we've already notified our callback and it isn't safe to do so again.
if (stage != Stage.ENCODE) {
notifyFailed();
}
if (!isCancelled) {
throw e;
}
}
}
然後在看裏面的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);
}
}
先看getNextStage方法:
// 這裏的階段策略首先是從resource中尋找,然後再是data,,再是source
private Stage getNextStage(Stage current) {
switch (current) {
case INITIALIZE:
// 根據定義的緩存策略來回去下一個狀態
// 緩存策略來之於RequestBuilder的requestOptions域
// 如果你有自定義的策略,可以調用RequestBuilder.apply方法即可
// 詳細的可用緩存策略請參看DiskCacheStrategy.java
return diskCacheStrategy.decodeCachedResource()
? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
case RESOURCE_CACHE:
return diskCacheStrategy.decodeCachedData()
? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
case DATA_CACHE:
return Stage.SOURCE;
case SOURCE:
case FINISHED:
return Stage.FINISHED;
default:
throw new IllegalArgumentException("Unrecognized stage: " + current);
}
}
再看getNextGenerator函數,如下
// 根據Stage找到數據抓取生成器。
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
// 產生含有降低採樣/轉換資源數據緩存文件的DataFetcher。
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
// 產生包含原始未修改的源數據緩存文件的DataFetcher。
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
// 生成使用註冊的ModelLoader和加載時提供的Model獲取源數據規定的DataFetcher。
// 根據不同的磁盤緩存策略,源數據可首先被寫入到磁盤,然後從緩存文件中加載,而不是直接返回。
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
經過上面的流程,最後就是發起實際請求的地方了,SourceGenerator.startNext()方法。
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
這裏的Model必須是實現了GlideModule接口的,fetcher是實現了DataFetcher接口。有興趣同學可以繼續看一下integration中的okhttp和volley工程。Glide主要採用了這兩種網絡libray來下載圖片。
數據下載完成後緩存處理SourceGenerator.onDataReady
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
這裏將下載的data存到磁盤cache中,但是咋就一句dataToCache = data,其實是在cb.reschedule(),cb就是decodeJob。
public void reschedule() {
runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
callback.reschedule(this);
}
這裏又有一個Callback,繼續追蹤,這裏的Callback接口是定義在DecodeJob內的,而實現是在外部的Engine中(這裏會用線程池重新啓動當前job,那爲什麼要這樣做呢?源碼中的解釋是爲了不同線程的切換,因爲下載都是借用第三方網絡庫,而實際的編解碼是在Glide自定義的線程池中進行的)
public void reschedule(DecodeJob<?> job) {
if (isCancelled) {
MAIN_THREAD_HANDLER.obtainMessage(MSG_CANCELLED, this).sendToTarget();
} else {
sourceExecutor.execute(job);
}
}
接下來繼續DecodeJob.runWrapped()方法。這個時候的runReason是SWITCH_TO_SOURCE_SERVICE,因此直接執行runGenerators(),這裏繼續執行SourceGenerator.startNext()方法,值得注意的dataToCache域,因爲上一次執行的時候是下載,因此再次執行的時候內存緩存已經存在,因此直接緩存數據cacheData(data):
private void cacheData(Object dataToCache) {
long startTime = LogTime.getLogTime();
try {
Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);
DataCacheWriter<Object> writer =
new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());
originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());
helper.getDiskCache().put(originalKey, writer);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished encoding source to cache"
+ ", key: " + originalKey
+ ", data: " + dataToCache
+ ", encoder: " + encoder
+ ", duration: " + LogTime.getElapsedMillis(startTime));
}
} finally {
loadData.fetcher.cleanup();
}
sourceCacheGenerator =
new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
}
我們在會到SourceGenerator.startNext()方法,這個時候已經有了sourceCacheGenerator,那麼直接執行DataCacheGenerator.startNext()方法:
@Override
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
// 這裏會通過model尋找註冊過的ModelLoader
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
// 通過FileLoader繼續加載數據
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
這裏的ModelLoader跟之前提到過的Register的模塊加載器(ModelLoader)對應是modelLoaderRegistry域,具體執行的操作是Registry.getModelLoaders(…)方法如下:
public <Model> List<ModelLoader<Model, ?>> getModelLoaders(Model model) {
List<ModelLoader<Model, ?>> result = modelLoaderRegistry.getModelLoaders(model);
if (result.isEmpty()) {
throw new NoModelLoaderAvailableException(model);
}
return result;
}
繼續回到DataCacheGenerator.startNext()方法,找到了ModelLoader,然後跟蹤到的是FileLoader類(FileFetcher.loadData(…)方法):
public void loadData(Priority priority, DataCallback<? super Data> callback) {
// 讀取文件數據
try {
data = opener.open(file);
} catch (FileNotFoundException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to open file", e);
}
//失敗
callback.onLoadFailed(e);
return;
}
// 成功
callback.onDataReady(data);
}
裝載流程
代碼太多,實在有點繞暈了,所以就將一位大神的博客裏面裝載流程粘了過來。
主要線路如下:
–>DataCacheGenerator.onDataReady
–>SourceGenerator.onDataFetcherReady
–>DecodeJob.onDataFetcherReady
–>DecodeJob.decodeFromRetrievedData
–>DecodeJob.notifyEncodeAndRelease
–>DecodeJob.notifyComplete
–>EngineJob.onResourceReady
需要說明的就是在EngineJob中有一個Handler叫MAIN_THREAD_HANDLER。爲了實現在主UI中裝載資源的作用,ok繼續上邊的流程:
–>EngineJob.handleResultOnMainThread
–>SingleRequest.onResourceReady
–>ImageViewTarget.onResourceReady
–>ImageViewTarget.setResource
–>ImageView.setImageDrawable/ImageView.setImageBitmap
數據的裝載過程中有一個很重要的步驟就是decode,這個操作發生在DecodeJob.decodeFromRetrievedData的時候,繼續看代碼:
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,
"data: " + currentData
+ ", cache key: " + currentSourceKey
+ ", fetcher: " + currentFetcher);
}
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
exceptions.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
這中間發生了很多轉換主要流程:
–>DecodeJob.decodeFromData
–>DecodeJob.decodeFromFetcher
–>DecodeJob.runLoadPath
–>LoadPath.load
–>LoadPath.loadWithExceptionList
–>LoadPath.decode
–>LoadPath.decodeResource
–>LoadPath.decodeResourceWithList
–>ResourceDecoder.handles
–>ResourceDecoder.decode
這裏講到了decode,那麼encode發生在什麼時候呢?直接通過Encoder接口調用發現,在數據緩存的時候纔會觸發編碼。具體調用在DiskLruCacheWrapper和DataCacheWriter中。一些值得參考的寫法例如BitmapEncoder對Bitmap的壓縮處理。
結束語
總結,glide庫很大,自己在看源碼的時候,也參考了別人對glide的理解,將glide的大概流程的主要源碼看了一下,其中可能會有很多理解不對或者不足的地方,希望大家能給指出來。
下面是是一位大神看完glide之後的結束語,個人覺得還是很不錯的。
1、總體來說代碼寫的挺漂亮的,單從使用者角度來說入手是比較容易的。
2、源碼使用了大量的工廠方法來創建對象,就像String.valueof(…)方法一樣,這也體現編碼的優雅。
3、不過想要對這個庫進行改造,可能並非易事,筆者在跟蹤代碼的過程中發現很多地方有Callback這樣的接口,來來回回查找幾次很容易就暈頭轉向了。。。
另外一個感覺難受的地方就是構造方法帶入參數太多,就拿SingleRequest來說就是12個構造參數。
4、單例的使用感覺還是有些模糊,就比如GlideContext,有些時候通過Glide.get(context).getGlideContext()獲取,而有些類中採用構造傳入。個人覺得既然讓Glide作爲單例,那麼還這樣傳入參數是不是有點多餘?代碼的編寫都是可以折中考慮,不過如果整個項目擬定好了一個規則的話,我想最好還是遵循它。另外再吐槽一下單例,很多開發人員喜歡用單例,如果你是有代碼潔癖的開發者,那麼你肯定很討厭這樣,單例很容易造成代碼的散落和結構不清晰。