最近比較無聊,爲了找點事幹,就花了兩天時間把Glide的源碼大概看了一下。剛開始看Glide的源碼頭腦仍是比較亂的,由於做者引入了幾個概念,又大量用了泛型,若是不瞭解這些概念讀起代碼來就比較痛苦,我也沒有詳細看各類實現細節的東西,只是瞭解了下這個框架的大概樣子,在這篇文章裏,我會介紹下Glide中的一些關鍵概念,並走一遍圖片加載流程,若是你要閱讀Glide源碼的話,應該多少會有點幫助。java
首先是三個最基本的概念:Model
, Data
和Resource
。android
想一下,咱們加載圖片須要什麼?通常是一個url,但url並非全部狀況,還有資源ID,文件等等,甚至能夠是Feed流中的一條Feed,雖然通常咱們會從Feed中取出圖片的url來轉換爲從url中加載的狀況,Glide把這些抽像爲了一個概念,就是Model
,因此Model
就是數據地址的最初來源。緩存
Model
並不能直接解析爲圖片,好比一個url,是要轉換爲網絡流的InputStream才能被解析爲圖片的,Model
須要進行一次轉換才能作爲數據解析的數據源,這些轉換後的東西就叫作Data
,Glide並無一個Data類,但有不少和它相關的概念,如dataClase,DataFetcher等。網絡
那麼Resource
呢,其實它就是一個包裝類,一個wrapper,它wrap一個對象,使這個對象能夠經過對象池進行緩存與重用。app
這三個基本概念介紹完了,接下來看一下Glide基本框架。框架
作爲一個圖片加載框架,確定會包含緩存部分。ide
能夠從網上很容易的瞭解到,Glide的磁盤緩存能夠緩存原始數據,也能夠緩存處理過的數據。什麼意思呢,就是你有一張1000x1000的圖片,但你是在列表中展現的,好比是200x200,那麼緩存時能夠直接將整個網絡流緩存下來,即1000x1000的圖片,要展現的時候再縮放,但這就下降了展現效率,因此Glide也能夠把處理過的200x200的圖片緩存起來,增長了緩存大小,但優化了展現速度。函數
至於怎麼把數據緩存到磁盤,就引入了一個叫Encoder
的概念,Encoder
是用來持久化數據的。fetch
但看源碼時你會發現,Glide中有一個類叫Registry
,能夠註冊多個Encoder
,但你會發現它還能夠註冊ResourceEncoder
。這兩個Encoder
很容易引發混淆,而其實ResouseEncoder
繼承自Encoder
。Encoder
是用來持久化Data
的,ResourceEncoder
是用來持久化Resource
的。看Glide默認註冊的Encoder
就知道了,默認註冊的Encoder
爲ByteBuffer
和InputStream
,而ResourceEncoder
是Bitmap
、BitmapDrawable
和GifDrawable
,也就是一個持久化原始數據,一個持久化處理過的數據。我感受把Encoder
作爲一個上級的抽象,引入一個和ResourceEncoder
同級的DataEncoder
就好理解了,正好和前面的基本概念Data
和Resource
對應。優化
有Encoder
就有Decoder
,對應的類叫ResourceDecoder
,用來將數據(InputStream等)解析爲Resource
。
圖片加載出來後還可能會應用各類變換,如圓角圖片,圓形圖片,處理這部分工做的叫Transformation
基礎概念介紹的差很少了,加載流程也差很少出來了:
但咱們發現前面的介紹中少了一環,即:Glide是怎麼把Model
轉換爲Data
的。這就引入另外一個概念,ModelLoader
,就是把Model
轉換成Data
的,爲了方便說明,直接把這個類的代碼貼上來了,去掉了一些註釋。
/** * A factory interface for translating an arbitrarily complex data model into a concrete data type * that can be used by an {@link DataFetcher} to obtain the data for a resource represented by the * model. * * @param <Model> The type of the model. * @param <Data> The type of the data that can be used by a * {@link com.bumptech.glide.load.ResourceDecoder} to decode a resource. */ public interface ModelLoader<Model, Data> { /** * Contains a set of {@link com.bumptech.glide.load.Key Keys} identifying the source of the load, * alternate cache keys pointing to equivalent data, and a * {@link com.bumptech.glide.load.data.DataFetcher} that can be used to fetch data not found in * cache. * * @param <Data> The type of data that well be loaded. */ class LoadData<Data> { public final Key sourceKey; public final List<Key> alternateKeys; public final DataFetcher<Data> fetcher; public LoadData(Key sourceKey, DataFetcher<Data> fetcher) { this(sourceKey, Collections.<Key>emptyList(), fetcher); } public LoadData(Key sourceKey, List<Key> alternateKeys, DataFetcher<Data> fetcher) { this.sourceKey = Preconditions.checkNotNull(sourceKey); this.alternateKeys = Preconditions.checkNotNull(alternateKeys); this.fetcher = Preconditions.checkNotNull(fetcher); } } LoadData<Data> buildLoadData(Model model, int width, int height, Options options); boolean handles(Model model); }
ModelLoader
有兩個方法,一個handles
表示是否能夠處理這個類型的Model
,若是能夠的話就能夠經過buildLoadData
生成一個LoadData
,而LoadData
包含了要用來作緩存的key,及用來獲取數據的DataFetcher
。
到這裏,整個加載流程就清楚了:
接下來要作的就是根據咱們的使用方法走一遍流程,調用以下:
Glide.with(mContext) .load(url) .apply(RequestOptions.placeholderOf(R.drawable.loading)) .into(myImageView);
一步步看,先是Glide.with(mContext)
:
public static RequestManager with(Context context) { RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(context); }
經過RequestManagerRetriever
獲取到了一個RequestManager
,至於爲何還須要一個RequestManagerRetriever
並有各類重載方法,主要是由於Glide經過SupportRequestManagerFragment
和RequestManagerFragment
關聯了Activity或Fragment的生命週期,用來作pauseRequests
等操做。
而後是load
:
public RequestBuilder<Drawable> load(@Nullable Object model) { return asDrawable().load(model); } public RequestBuilder<Drawable> asDrawable() { return as(Drawable.class).transition(new DrawableTransitionOptions()); } public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) { return new RequestBuilder<>(glide.getGlideContext(), this, resourceClass); }
是asDrawable.load(model)
的縮寫,就是說這個Model我是要加載爲Drawable的,最終返回一個RequestBuilder
,看名字就知道是作什麼了,不過這個類主要是設置Thumbnail Request,Transition等個別設置(舊版本中placeHolder等也是在這裏設置的),大部分設置在RequestOptions
裏,這就是下面這一句:
apply(RequestOptions.placeholderOf(R.drawable.loading))
應用一個RequestOptions
,RequestOptions
能夠設置各類請求相關的選項,如佔位圖片,加載失敗的圖片,緩存策略等。RequestOptions
繼承自BaseRequestOptions
,但全是工廠方法生成各類RequestOptions。
最後就是into
了,把圖片加載到一個Target
中。
public Target<TranscodeType> into(ImageView view) { ... return into(context.buildImageViewTarget(view, transcodeClass)); } public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) { Util.assertMainThread(); Preconditions.checkNotNull(target); if (!isModelSet) { throw new IllegalArgumentException("You must call #load() before calling #into()"); } Request previous = target.getRequest(); if (previous != null) { requestManager.clear(target); } requestOptions.lock(); Request request = buildRequest(target); target.setRequest(request); requestManager.track(target, request); return target; }
Target
是要加載到的目標,好比ImageViewTarget
,AppWidgetTarget
,在這裏咱們傳進來了一個ImageView
,內部生成了一個DrawableImageViewTarget
。這裏最主要的操做是buildRequest
而後交給RequestManager
去track
。
void track(Target<?> target, Request request) { targetTracker.track(target); requestTracker.runRequest(request); } // RequestTracker public void runRequest(Request request) { requests.add(request); if (!isPaused) { request.begin(); } else { pendingRequests.add(request); } }
TargetTracker
主要就是記錄一下全部正在加載的圖片的Target
,因此加載行爲是在RequestTracker.runRequest
中的,runRequest
先判斷是不是pause狀態(RequestManager設置),若是不是就直接調用Request.begin
觸發加載,不然就回到pending隊列裏等待resume。
除了設置縮略圖的情景,使用的Request
都是SingleRequest
,看一下它的begin
方法:
public void begin() { stateVerifier.throwIfRecycled(); startTime = LogTime.getLogTime(); 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; 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 (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } }
加載邏輯是這幾行:
if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { target.getSize(this); }
判斷下是否知道Target
的大小,若是大小已知就調用onSizeReady
,不然就調用target.getSize
獲取它的大小,當成功獲取到大小後,會經過回調繼續調用onSizeReady
,因此整個加載方法都是在onSizeReady
裏的。至於Target
怎麼獲取它的大小,那要看它的實現了,對於ImageViewTarget
,是經過ViewTreeObserver.OnPreDrawListener
等到View要測繪的時候就知道它的大小了。
onSizeReady
就是把操做轉移到了Engine.load
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, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, ResourceCallback cb) { Util.assertMainThread(); long startTime = LogTime.getLogTime(); EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); 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; } 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; } 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); }
在Engine.load
中,先loadFromCache
,若是緩存沒有命中就再loadFromActiveResources
,這是兩級內存緩存,第一級是LruCache,第二級是ActiveCache,主要做用是,有可能一個圖片很早就被加載了,可能已經從LruCache被移除掉了,但這個圖片可能還在被某一個地方引用着,也就是仍是Active的,那它就可能在未來仍被引用到,因此就把它保留在二級的ActiveCache中,ActiveCache中是以弱引用引用圖片的,並經過ReferenceQueue
監測弱引用的回收,而後用Handler.IdleHandler
在CPU空閒時被被回收的引用項從ActiveCache中移除。
接下來看對應的Key是否已經正在加載,若是是的話,就addCallback
,這樣若是有多個地方同時請求同一張圖片的話,只會生成一個加載任務,並都能收到回調,這點是比Universal-Image-Loader好的地方。
正常的加載流程是生成一個EngineJob
和一個DecodeJob
,經過engineJob.start(decodeJob)
來進行實際的加載。
public void start(DecodeJob<R> decodeJob) { this.decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); }
EngineJob.start
直接將DecodeJob
交給Executor去執行了(DecodeJob
實現了Runnable
接口)。DecodeJob
的加載操做放到了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); } } private DataFetcherGenerator getNextGenerator() { switch (stage) { case RESOURCE_CACHE: return new ResourceCacheGenerator(decodeHelper, this); case DATA_CACHE: return new DataCacheGenerator(decodeHelper, this); case SOURCE: return new SourceGenerator(decodeHelper, this); case FINISHED: return null; default: throw new IllegalStateException("Unrecognized stage: " + stage); } } private Stage getNextStage(Stage current) { switch (current) { case INITIALIZE: 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); } }
主要加載邏輯就在這三個函數中了:
一共有三種Generator:
ResourceCacheGenerator
:從處理過的緩存加載數據DataCacheGenerator
:從原始緩存加載數據SourceGenerator
:從數據源請求數據,如網絡請求前面說過,Glide的磁盤緩存能夠選擇緩存原始圖片,緩存處理過的圖片(如列表中顯示縮略圖時縮放後的圖片),這三個Generator就分別對應處理過的圖片緩存,原始圖片緩存,和數據源加載。
在上面的第三步執行Generator時主要就是調用了Generator,其實就是執行Generator的startNext
方法,這裏以SourceGenerator
爲例。
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; }
先忽略函數開始時dataToCache
和sourceCacheGenerator
相關的代碼,第一次加載時這兩個必定是null的。剩下的流程就是獲取一個LoadData
,調用LoadData.fetcher.loadData
加載數據。看一下LoadData
List<LoadData<?>> getLoadData() { if (!isLoadDataSet) { isLoadDataSet = true; loadData.clear(); List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model); int size = modelLoaders.size(); for (int i = 0; i < size; i++) { ModelLoader<Object, ?> modelLoader = modelLoaders.get(i); LoadData<?> current = modelLoader.buildLoadData(model, width, height, options); if (current != null) { loadData.add(current); } } } return loadData; }
在getLoadData
中經過獲取全部提早註冊過的能處理Model
類型的ModelLoader
,調用它的buildLoadData
生成LoadData
,最終返回一個LoadData
列表。
前面說過LoadData
包含了用來獲取數據的DataFetcher
。SourceGenerator.startNext
就調用了loadData.fetcher.loadData
來進行加載數據,並傳進去一個Callback,就是當前的SourceGenerator
,若是加載成功,會調用onDataReady
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); } }
數據加載成功後,若是設置了要進行磁盤緩存,會設置成員變量dataToCache
,並調用Callback的reschedule
,結果就是會再次調用當前Generator的startNext
,startNext
的前半部分實現就起做用了,會進行寫緩存的操做。
當rescheudle
後寫了緩存後,或不緩存的狀況下,會調用onDataFetcherReady
,這個Callback就是前面的DecodeJob
,在onDataFetcherReady
中會調用decodeFromRetrievedData
decode數據,最終調用到decodeFromFetcher
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource) throws GlideException { LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass()); return runLoadPath(data, dataSource, path); }
獲取LoadPath
,並調用它的load
方法。LoadPath
就是封裝了多個DecodePath
,DecodePath
用於decode and Transform數據,如InputStream->Bitmap->BitmapDrawable,DecodePath
中會獲取預先註冊的Decoder
來decode獲取到的數據,decode成功後經過回調調用DecodeJob
的onResourceDecoded
方法。
public Resource<Z> onResourceDecoded(Resource<Z> decoded) { Class<Z> resourceSubClass = getResourceClass(decoded); Transformation<Z> appliedTransformation = null; Resource<Z> transformed = decoded; if (dataSource != DataSource.RESOURCE_DISK_CACHE) { appliedTransformation = decodeHelper.getTransformation(resourceSubClass); transformed = appliedTransformation.transform(decoded, width, height); ////////////////////////// 1 } // TODO: Make this the responsibility of the Transformation. if (!decoded.equals(transformed)) { decoded.recycle(); } final EncodeStrategy encodeStrategy; final ResourceEncoder<Z> encoder; if (decodeHelper.isResourceEncoderAvailable(transformed)) { encoder = decodeHelper.getResultEncoder(transformed); encodeStrategy = encoder.getEncodeStrategy(options); } else { encoder = null; encodeStrategy = EncodeStrategy.NONE; } Resource<Z> result = transformed; boolean isFromAlternateCacheKey = !decodeHelper.isSourceKey(currentSourceKey); if (diskCacheStrategy.isResourceCacheable(isFromAlternateCacheKey, dataSource, encodeStrategy)) { if (encoder == null) { throw new Registry.NoResultEncoderAvailableException(transformed.get().getClass()); } final Key key; if (encodeStrategy == EncodeStrategy.SOURCE) { key = new DataCacheKey(currentSourceKey, signature); } else if (encodeStrategy == EncodeStrategy.TRANSFORMED) { key = new ResourceCacheKey(currentSourceKey, signature, width, height, appliedTransformation, resourceSubClass, options); } else { throw new IllegalArgumentException("Unknown strategy: " + encodeStrategy); } LockedResource<Z> lockedResult = LockedResource.obtain(transformed); deferredEncodeManager.init(key, encoder, lockedResult); ////////////////////////// 2 result = lockedResult; } return result; }
在上述代碼的註釋1處對加載成功的資源應用Transformation,而後在註釋2處根據緩存策略初始化DeferredEncodeManager
,在前面的decodeFromRetrievedData
中,若是有必要會把transform過的資源寫緩存。
private void decodeFromRetrievedData() { ... if (resource != null) { notifyEncodeAndRelease(resource, currentDataSource); } else { runGenerators(); } }
notifyEncodeAndRelease
中處理了對處理過的圖片的緩存操做。當緩存完成後(若是有須要的話)就經過回調告訴外面加載完成了。至此,整個加載過程完成。
Glide容許咱們進行必定程度的自定義,好比設置自定義的Executor,設置緩存池,設置Log等級等,完成這個任務的類叫GlideBuilder
,Glide類在工程中是做爲單例使用的,看一下代碼:
public static Glide get(Context context) { if (glide == null) { synchronized (Glide.class) { if (glide == null) { Context applicationContext = context.getApplicationContext(); List<GlideModule> modules = new ManifestParser(applicationContext).parse(); GlideBuilder builder = new GlideBuilder(applicationContext); for (GlideModule module : modules) { module.applyOptions(applicationContext, builder); } glide = builder.createGlide(); for (GlideModule module : modules) { module.registerComponents(applicationContext, glide.registry); } } } } return glide; }
經過GlideBuilder
生成了一個Glide
實例,咱們是沒有辦法直接配置GlideBuilder
的,但咱們發現Glide.get
解析了Manifest,獲取了一個GlideModule
的列表,並調用了它的applyOptions
和registerComponents
方法。以項目中OkHttp的配置爲例
public class OkHttpGlideModule implements GlideModule { @Override public void applyOptions(Context context, GlideBuilder builder) { // Do nothing. } @Override public void registerComponents(Context context, Registry registry) { registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory()); } }
GlideModule
有兩個方法,applyOptions
,有一個GlideBuilder
參數,在這裏咱們就能夠配置Glide了。還有一個registerComponents
方法,並有一個Registry
參數,經過這個類的實例咱們就能夠註冊咱們自定義的ModelLoader
,Encoder
等基礎組件了。
自定義GlideModule
是經過Manifest的meta-data標籤配置的
<meta-data android:name="com.bumptech.glide.integration.okhttp3.OkHttpGlideModule" android:value="GlideModule"/>
http://www.lightskystreet.com/2015/10/12/glide_source_analysis/