Glide 源碼
Glide是google開源的一款圖片加載框架,注重性能和加載速度称近。本身采用流式Api便于操作。本文根據(jù)Glide源碼谦疾,分析一下Glide的內(nèi)部實現(xiàn)。源碼基于glide 3.7
with()
Glide的使用從Glide.with(Context context)開始犬金,with是Glide的一個靜態(tài)方法念恍,參數(shù)為context,內(nèi)部Glide會根據(jù)傳入的不同context進(jìn)行不同的操作晚顷。
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
RequestManagerRetriever用于生成RequestManager樊诺,跟進(jìn)RequestManagerRetriever的get()方法:
public RequestManager get(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) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
//以Activity舉例
@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);
}
}
可以看到get方法根據(jù)不同的context種類進(jìn)行不同的操作,如果是ApplicationContext或者在后臺線程創(chuàng)建的Glide會默認(rèn)走getApplicationManager()方法音同。
接下來進(jìn)入fragmentGet()方法:
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
getRequestManagerFragment方法使用默認(rèn)的tag通過findFragmentByTag獲取RequestManagerFragment词爬。如果沒有則新建一個返回。這里問什么要創(chuàng)建一個RequestManagerFragment呢权均?其實RequestManagerFragment是Fragment的一個子類顿膨,RequestManager是它的成員變量。Glide這里的做法很巧妙叽赊,通過創(chuàng)建一個與當(dāng)前Activity綁定的沒有布局的Fragment用于監(jiān)聽Activity的生命周期恋沃,并在一些特定時期調(diào)用RequestManager的方法。比如必指,F(xiàn)ragment實現(xiàn)了ComponentCallbacks2接口囊咏,有兩個方法onTrimMemory()和onLowMemory()。用于在內(nèi)存不足時調(diào)用RequestManager的方法用于釋放內(nèi)存。因為Glide內(nèi)部對Activity的生命周期進(jìn)行監(jiān)聽梅割,在onDestory()的時候釋放了資源并調(diào)用了RequestTacker的clearRequests()方法取消網(wǎng)絡(luò)請求霜第,防止內(nèi)存泄漏。所以對于使用者來說不用手動的在onDestory()中對Glide進(jìn)行操作户辞。這也解釋了為什么在子線程創(chuàng)建會傳入ApplicationContext泌类,使Glide與整個應(yīng)用的生命周期保持一致。
注意到在獲取RequestManager的時候有一個assertNotDestroyed(activity)方法:
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static void assertNotDestroyed(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
}
}
在實際開發(fā)中在網(wǎng)絡(luò)比較慢的時候比如去服務(wù)器請求圖片url比較慢底燎,當(dāng)請求過程中按下了返回鍵刃榨,url返回調(diào)用Glide加載時activity已經(jīng)銷毀,將會拋出異常双仍。這里的解決思路是盡可能早的把RequestManager創(chuàng)建出來枢希,比如可以在Activity的onCreate()方法或者Fragment的onAttach()方法進(jìn)行RequestManager的初始化操作。
RequestManager manager = Glide.with(this);
load()
通過Glide.with()方法得到RequestManager后朱沃,調(diào)用RequestManager的load方法進(jìn)行圖片加載苞轿。
load()有多個重載方法,分別對應(yīng)從uri为流、url呕屎、文件让簿、資源id等地方進(jìn)行加載敬察。我們選取最常用的從url加載進(jìn)行進(jìn)一步分析。
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}
return optionsApplier.apply(
new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
glide, requestTracker, lifecycle, optionsApplier));
}
與從url加載類似尔当,其他集中l(wèi)oad()方法同樣最后調(diào)用loadGeneric()方法莲祸,傳入不同的class對象,構(gòu)建不同的ModelLoader椭迎。loadGeneric()方法構(gòu)建兩個ModelLoader锐帜,新建一個DrawableTypeRequest并返回。ModelLoader在Glide中是一個比較重要的概念畜号,作用是從原始數(shù)據(jù)源中取出數(shù)據(jù)缴阎。即把抽象的數(shù)據(jù)模型轉(zhuǎn)化成具體的數(shù)據(jù),一般為InputSteam简软,比如根據(jù)url網(wǎng)絡(luò)請求蛮拔。
實際上,Glide 在初始化的時候,對于每種類型的輸入:String痹升、int建炫、Integer、File疼蛾、Uri肛跌,都注冊了能夠?qū)⑺鼈冝D(zhuǎn)化為 InputStream 和 ParcelFileDescriptor 的 ModelLoader,保存在HashMap中。這兩個 buildxxxModelLoader() 方法實際上就是從 HashMap 中獲取對應(yīng)的 ModelLoader衍慎。在當(dāng)前情景下转唉,獲取到的是 StreamStringLoader 和 FileDescriptorStringLoader。
buildxxxModelLoader內(nèi)部實現(xiàn)都是一樣的西饵,都是先從緩存中取酝掩,如果取不到通過Factory創(chuàng)建一個并加入到緩存中,以buildStreamModelLoader()為例:
public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
return buildModelLoader(modelClass, InputStream.class, context);
}
public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass,
Context context) {
if (modelClass == null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Unable to load null model, setting placeholder only");
}
return null;
}
return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
}
public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass) {
ModelLoader<T, Y> result = getCachedLoader(modelClass, resourceClass);
if (result != null) {
if (NULL_MODEL_LOADER.equals(result)) {
return null;
} else {
return result;
}
}
final ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass);
if (factory != null) {
result = factory.build(context, this);
cacheModelLoader(modelClass, resourceClass, result);
} else {
cacheNullLoader(modelClass, resourceClass);
}
return result;
}
Glide.get(context).getLoaderFactory()工廠GenericLoaderFactory眷柔,調(diào)用工廠的buildModelLoader()方法期虾,首先通過getCacheLoader方法判斷緩存是否存在。如果不存在驯嘱,同樣從緩存中獲取ModelLoaderFactory镶苞,調(diào)用build()方法,添加緩存并返回當(dāng)前ModelLoader鞠评。因為當(dāng)前的modelClass為url即為String.class茂蚓,所以最終得到StreamStringLoader。
同理剃幌,得到FileDescriptorModelLoader聋涨,并最終構(gòu)建出DrawableTypeRequest「合纾回到RequestManager的load()方法牍白,調(diào)用了DrawableTypeRequest的load()方法,DrawableTypeRequest繼承自DrawableRequestBuilder抖棘,最終調(diào)用了DrawableRequestBuilder的父類GenericRequestBuilder的load()方法:
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}
load()方法里只做了一些復(fù)制操作茂腥。至此load()方法告一段落,主要是根據(jù)數(shù)據(jù)源類型構(gòu)建ModelLoader切省,并創(chuàng)建一個DrawableTypeRequest最岗。
into()
load()方法返回了一個DrawableTypeRequest對象,into()是它的父類DrawableRequestBuilder的方法朝捆,內(nèi)部調(diào)用了父類的into()方法般渡,即GenericRequestBuilder。
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
into()方法中芙盘,如果當(dāng)前ImageView有設(shè)置ScaleType驯用,則進(jìn)行對應(yīng)的裁剪操作。以applyCenterCrop()為例何陆,發(fā)現(xiàn)GenericRequestBuilder的applyCenterCrop()由它的三個子類來實現(xiàn):DrawableRequestBuilder晨汹、BitmapRequestBuilder、GifRequestBuilder贷盲。DrawableRequestBuilder上節(jié)我們提到了淘这,BitmapRequestBuilder和GifRequestBuilder是怎么得到的呢剥扣?答案在DrawableTypeRequest里。DrawableTypeRequest里有兩個方法asBitmap()铝穷、asGif()分別得到BitmapRequestBuilder和GifRequestBuilder钠怯。以常規(guī)的DrawableRequestBuilder的applyCenterCrop()為例:
@Override
void applyCenterCrop() {
centerCrop();
}
public DrawableRequestBuilder<ModelType> centerCrop() {
return transform(glide.getDrawableCenterCrop());
}
public DrawableRequestBuilder<ModelType> transform(Transformation<GifBitmapWrapper>... transformation) {
super.transform(transformation);
return this;
}
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transform(
Transformation<ResourceType>... transformations) {
isTransformationSet = true;
if (transformations.length == 1) {
transformation = transformations[0];
} else {
transformation = new MultiTransformation<ResourceType>(transformations);
}
return this;
}
經(jīng)過層層調(diào)用,最后到了GenericRequestBuilder的transform方法曙聂,transform方法只是一個簡單的賦值操作晦炊,具體的轉(zhuǎn)換操作不是在這里進(jìn)行的。
回到into()方法調(diào)用了glide中的buildImageViewTarget()方法宁脊,構(gòu)建一個ImageViewTarget:
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
在ImageViewTargetFactory的buildTarget方法中断国,根據(jù)transcodedClass的類型構(gòu)造出不同的ImageViewTarget,transcodedClass是GenericRequestBuilder的構(gòu)造器傳進(jìn)來的榆苞,即上文提到的asBitmap()稳衬、asGif()。
得到ImageViewTarget后調(diào)用GenerocRequestBuilder中的into()的重載方法:
public <Y extends Target<TranscodeType>> Y into(Y target) {
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);
requestTracker.runRequest(request);
return target;
}
Glide 通過 View 的 setTag() 方法將特定的 View 和其對應(yīng)的圖片加載請求綁定在一起坐漏。這樣做的好處是能夠容易地判斷 Target 所封裝的 View 是否被復(fù)用薄疚,復(fù)用時先取消之前的請求,避免了不必要的請求赊琳,也能防止圖片錯位街夭。
構(gòu)建新的Request對象:
private Request buildRequest(Target<TranscodeType> target) {
if (priority == null) {
priority = Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}
private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
if (thumbnailRequestBuilder != null) {
if (isThumbnailBuilt) {
throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, "
+ "consider using clone() on the request(s) passed to thumbnail()");
}
// Recursive case: contains a potentially recursive thumbnail request builder.
if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
thumbnailRequestBuilder.animationFactory = animationFactory;
}
if (thumbnailRequestBuilder.priority == null) {
thumbnailRequestBuilder.priority = getThumbnailPriority();
}
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
thumbnailRequestBuilder.overrideHeight)) {
thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
}
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
// Guard against infinite recursion.
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
isThumbnailBuilt = false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
}
}
首先是與縮略圖有關(guān)的邏輯,即是否調(diào)用thumbnail()方法躏筏,如果有縮略圖構(gòu)建兩個request板丽。如果沒有,調(diào)用obtainRequest()方法,obtainRequest()方法構(gòu)建了一個GenericRequest對象寸士。接下來將target與request綁定起來檐什,設(shè)置監(jiān)聽碴卧,調(diào)用RequestTracker的runRequest()方法弱卡。
RequestTracker是一個用來跟蹤、取消或重啟一個正在進(jìn)行住册、完成或失敗的請求婶博。
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
把請求加入請求隊列中,如果tracker沒有被停止荧飞,調(diào)用request的begin()方法凡人,如果停止了,就加入到準(zhǔn)備請求隊列中叹阔。
Request的實現(xiàn)類GenericRequest中的begin()方法:
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
begin()方法主要確定請求圖片的大小挠轴,即有沒有調(diào)用override()方法,以及設(shè)置占位圖耳幢。如果沒有調(diào)用override方法岸晦,則調(diào)用target.getSize()方法(target父類ViewTarget):
public void getSize(SizeReadyCallback cb) {
sizeDeterminer.getSize(cb);
}
public void getSize(SizeReadyCallback cb) {
int currentWidth = getViewWidthOrParam();
int currentHeight = getViewHeightOrParam();
if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) {
cb.onSizeReady(currentWidth, currentHeight);
} 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);
}
}
}
根據(jù)view的寬高以及LayoutParam得到圖片寬高欧啤,調(diào)用SizeReadyCallback的onSizeReady()方法。GenericRequest實現(xiàn)了SizeReadyCallback接口启上,onSizeReady方法與重寫了圖片寬高所調(diào)用的onSizeReady()方法一致邢隧。
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
計算過寬高之后通過loadProvider得到當(dāng)前ModelLoader。那么loadProvider從哪來呢冈在?在DrawableTypeRequest的構(gòu)造函數(shù)看到一個buildProvider()方法:
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
buildTranscoder()方法獲取將GifBitmapWrapper轉(zhuǎn)換成GlideDrawable的Transcoder倒慧,DataLoadProvider提供指定的類型的Data和Resource之間的編解碼,
在這里獲取的是ImageVideoWrapper與GlideDrawable之間的編解碼provider包券。ImageVideoModelLoader是streamModelLoader和fileDescriptorModelLoader的封裝纫谅。
回到onSizeReady()方法中,從loadProvider中獲取到ImageVideoModelLoader以及ResourceTranscoder溅固,并從ImageVideoModelLoader中獲取到一個ImageVideoFetcher系宜。調(diào)用Engine中的load()方法:
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()、loadFromActiveResources()指從緩存中讀取发魄,以后會展開分析盹牧。接著往下分析,根據(jù)生成的key查詢?nèi)蝿?wù)是否已經(jīng)存在励幼,如果存在汰寓,返回一個LoadStatus。LoadStatus指一個不再加載的請求苹粟。如果不存在有滑,新建一個。并創(chuàng)建一個DecodeJob和EngineRunnable嵌削,調(diào)用EngineJob的start()方法
EngineJob 負(fù)責(zé)統(tǒng)一管理加載請求的 ResourceCallback毛好,EngineJob 本身也實現(xiàn)了 ResourceCallback 接口,當(dāng)加載請求完成時 EngineRunnable 回調(diào) EngineJob 的 onResourceReady() 方法苛秕,EngineJob 在分發(fā)給所有的監(jiān)聽者肌访。DecodeJob 的工作特別繁重,負(fù)責(zé)了 Resource 的所有解碼工作艇劫,包括從 Data 解碼和從緩存解碼吼驶,同時承擔(dān)了解碼之后的轉(zhuǎn)換和轉(zhuǎn)碼工作。
start()方法內(nèi)部實現(xiàn)是把EngineRunnable提交給線程池店煞,由線程池調(diào)用EngineRunnable的run()方法:
@Override
public void run() {
if (isCancelled) {
return;
}
Exception exception = null;
Resource<?> resource = null;
try {
resource = decode();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception decoding", e);
}
exception = e;
}
if (isCancelled) {
if (resource != null) {
resource.recycle();
}
return;
}
if (resource == null) {
onLoadFailed(exception);
} else {
onLoadComplete(resource);
}
}
private Resource<?> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
private boolean isDecodingFromCache() {
return stage == Stage.CACHE;
}
private Resource<?> decodeFromCache() throws Exception {
Resource<?> result = null;
try {
result = decodeJob.decodeResultFromCache();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Exception decoding result from cache: " + e);
}
}
if (result == null) {
result = decodeJob.decodeSourceFromCache();
}
return result;
}
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
默認(rèn)的stage是從cache中取的蟹演,所以decode()方法中調(diào)用decodeFromCache(),如果在decodeFromCache中得到的result為空顷蟀,會走onLoadFailed()邏輯:
private void onLoadFailed(Exception e) {
if (isDecodingFromCache()) {
stage = Stage.SOURCE;
manager.submitForSource(this);
} else {
manager.onException(e);
}
}
@Override
public void submitForSource(EngineRunnable runnable) {
future = sourceService.submit(runnable);
}
如果從緩存中取不到酒请,則更改策略,從source中取鸣个。第二次調(diào)用decodeFromSource():
private Resource<?> decodeFromSource() throws Exception {
return decodeJob.decodeFromSource();
}
public Resource<Z> decodeFromSource() throws Exception {
Resource<T> decoded = decodeSource();
return transformEncodeAndTranscode(decoded);
}
private Resource<T> decodeSource() throws Exception {
Resource<T> decoded = null;
try {
long startTime = LogTime.getLogTime();
final A data = fetcher.loadData(priority);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Fetched data", startTime);
}
if (isCancelled) {
return null;
}
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
最終調(diào)用的是DecodeJob中的decodeSource()方法羞反。通過fetcher的loadData()方法獲取數(shù)據(jù)哮兰,這里的fetcher就是loadProvider中的ImageVideoFetcher:
@Override
public ImageVideoWrapper loadData(Priority priority) throws Exception {
InputStream is = null;
if (streamFetcher != null) {
try {
is = streamFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching input stream, trying ParcelFileDescriptor", e);
}
if (fileDescriptorFetcher == null) {
throw e;
}
}
}
ParcelFileDescriptor fileDescriptor = null;
if (fileDescriptorFetcher != null) {
try {
fileDescriptor = fileDescriptorFetcher.loadData(priority);
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Exception fetching ParcelFileDescriptor", e);
}
if (is == null) {
throw e;
}
}
}
return new ImageVideoWrapper(is, fileDescriptor);
}
從streamFetcher中獲取InputStream,此時的streamFetcher是HttpUrlFetcher對象苟弛,調(diào)用它的loadData()方法:
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new IOException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(2500);
urlConnection.setReadTimeout(2500);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new IOException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else {
if (statusCode == -1) {
throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
}
throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
}
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}
}
根據(jù)HttpUrlConnection獲取InputStream喝滞,封裝成ImageVideoWrapper并返回「囡回到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);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded from source", startTime);
}
}
return decoded;
}
首先進(jìn)行緩存操作右遭,然后從loadeProvider中獲取Decoder,進(jìn)行decode操作缤削。這里得到的Decoder是GifBitmapWrapperDrawableTranscoder對象窘哈,調(diào)用它的decode()方法:
@Override
public Resource<GifBitmapWrapper> decode(ImageVideoWrapper source, int width, int height) throws IOException {
ByteArrayPool pool = ByteArrayPool.get();
byte[] tempBytes = pool.getBytes();
GifBitmapWrapper wrapper = null;
try {
wrapper = decode(source, width, height, tempBytes);
} finally {
pool.releaseBytes(tempBytes);
}
return wrapper != null ? new GifBitmapWrapperResource(wrapper) : null;
}
private GifBitmapWrapper decode(ImageVideoWrapper source, int width, int height, byte[] bytes) throws IOException {
final GifBitmapWrapper result;
if (source.getStream() != null) {
result = decodeStream(source, width, height, bytes);
} else {
result = decodeBitmapWrapper(source, width, height);
}
return result;
}
private GifBitmapWrapper decodeStream(ImageVideoWrapper source, int width, int height, byte[] bytes)
throws IOException {
InputStream bis = streamFactory.build(source.getStream(), bytes);
bis.mark(MARK_LIMIT_BYTES);
ImageHeaderParser.ImageType type = parser.parse(bis);
bis.reset();
GifBitmapWrapper result = null;
if (type == ImageHeaderParser.ImageType.GIF) {
result = decodeGifWrapper(bis, width, height);
}
// Decoding the gif may fail even if the type matches.
if (result == null) {
// We can only reset the buffered InputStream, so to start from the beginning of the stream, we need to
// pass in a new source containing the buffered stream rather than the original stream.
ImageVideoWrapper forBitmapDecoder = new ImageVideoWrapper(bis, source.getFileDescriptor());
result = decodeBitmapWrapper(forBitmapDecoder, width, height);
}
return result;
}
private GifBitmapWrapper decodeGifWrapper(InputStream bis, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource<GifDrawable> gifResource = gifDecoder.decode(bis, width, height);
if (gifResource != null) {
GifDrawable drawable = gifResource.get();
// We can more efficiently hold Bitmaps in memory, so for static GIFs, try to return Bitmaps
// instead. Returning a Bitmap incurs the cost of allocating the GifDrawable as well as the normal
// Bitmap allocation, but since we can encode the Bitmap out as a JPEG, future decodes will be
// efficient.
if (drawable.getFrameCount() > 1) {
result = new GifBitmapWrapper(null /*bitmapResource*/, gifResource);
} else {
Resource<Bitmap> bitmapResource = new BitmapResource(drawable.getFirstFrame(), bitmapPool);
result = new GifBitmapWrapper(bitmapResource, null /*gifResource*/);
}
}
return result;
}
內(nèi)部調(diào)用了decodeStream()將InputStream轉(zhuǎn)成圖片。首先讀取stream前兩個字節(jié)判讀是否為Gif圖亭敢,如果是Gif滚婉,調(diào)用decodeGifWrapper(),如果不是調(diào)用decodeBitmapWrapper():
private GifBitmapWrapper decodeBitmapWrapper(ImageVideoWrapper toDecode, int width, int height) throws IOException {
GifBitmapWrapper result = null;
Resource<Bitmap> bitmapResource = bitmapDecoder.decode(toDecode, width, height);
if (bitmapResource != null) {
result = new GifBitmapWrapper(bitmapResource, null);
}
return result;
}
方法內(nèi)部調(diào)用了bitmapDecoder的decode方法,生成一個Resource對象帅刀,并封裝成GifBitmapWrapper让腹。這里的bitmapDecoder實際是ImageVideoBitmapDecoder對象,調(diào)用它的decode方法扣溺,最終調(diào)用了Downsampler的decode()方法:
public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
final ByteArrayPool byteArrayPool = ByteArrayPool.get();
final byte[] bytesForOptions = byteArrayPool.getBytes();
final byte[] bytesForStream = byteArrayPool.getBytes();
final BitmapFactory.Options options = getDefaultOptions();
RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(
is, bytesForStream);
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
try {
exceptionStream.mark(MARK_POSITION);
int orientation = 0;
try {
orientation = new ImageHeaderParser(exceptionStream).getOrientation();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot determine the image orientation from header", e);
}
} finally {
try {
exceptionStream.reset();
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Cannot reset the input stream", e);
}
}
}
options.inTempStorage = bytesForOptions;
final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
final int inWidth = inDimens[0];
final int inHeight = inDimens[1];
final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
final Bitmap downsampled =
downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize,
decodeFormat);
final Exception streamException = exceptionStream.getException();
if (streamException != null) {
throw new RuntimeException(streamException);
}
Bitmap rotated = null;
if (downsampled != null) {
rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
downsampled.recycle();
}
}
return rotated;
} finally {
byteArrayPool.releaseBytes(bytesForOptions);
byteArrayPool.releaseBytes(bytesForStream);
exceptionStream.release();
releaseOptions(options);
}
}
自此生成了需要的bitmap骇窍。
回到decodeFromSource()方法,接下來調(diào)用transformEncodeAndTranscode(decoded)方法:
private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded) {
long startTime = LogTime.getLogTime();
Resource<T> transformed = transform(decoded);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transformed resource from source", startTime);
}
writeTransformedToCache(transformed);
startTime = LogTime.getLogTime();
Resource<Z> result = transcode(transformed);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Transcoded transformed from source", startTime);
}
return result;
}
如果由transform锥余,調(diào)用transform方法腹纳,接著把轉(zhuǎn)換后的resource寫入到緩存。經(jīng)過一系列編解碼驱犹,最終回到了EngineRunnable的run()方法嘲恍,調(diào)用onLoadComplete():
private void onLoadComplete(Resource resource) {
manager.onResourceReady(resource);
}
manager是一個EngineRunnableManager接口,它的實現(xiàn)類是EngineJob雄驹,進(jìn)入EngineJob的onResourceReady()方法:
@Override
public void onResourceReady(final Resource<?> resource) {
this.resource = resource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
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;
// Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
// synchronously released by one of the callbacks.
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
}
onResourceReady()中發(fā)出一條消息佃牛,在handleResultOnMainThread()中調(diào)用了ResourceCallback的onResourceReady()方法,GenericRequest實現(xiàn)了ResourceCallback:
@Override
public void onResourceReady(Resource<?> resource) {
if (resource == null) {
onException(new Exception("Expected to receive a Resource<R> with an object of " + transcodeClass
+ " inside, but instead got null."));
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
onException(new Exception("Expected to receive an object of " + transcodeClass
+ " but instead got " + (received != null ? received.getClass() : "") + "{" + received + "}"
+ " inside Resource{" + resource + "}."
+ (received != null ? "" : " "
+ "To indicate failure return a null Resource object, "
+ "rather than a Resource object containing null data.")
));
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't set the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady(resource, (R) received);
}
private void onResourceReady(Resource<?> resource, R result) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
isFirstResource)) {
GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstResource);
target.onResourceReady(result, animation);
}
notifyLoadSuccess();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
+ (resource.getSize() * TO_MEGABYTE) + " fromCache: " + loadedFromMemoryCache);
}
}
調(diào)用了target.onResourceReady(result, animation)荠医,target就是我們前面所提到的把ImageView封裝成的ImageViewTarget:
@Override
public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
if (glideAnimation == null || !glideAnimation.animate(resource, this)) {
setResource(resource);
}
}
protected abstract void setResource(Z resource);
以BitmapImageViewTarget為例:
@Override
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}
最終圖片顯示出來吁脱。
參考文章