Glide-原始碼分析(一)
前言
前面幾片文章主要介紹了下Picasso
,相對來說Picasso
原始碼看起來會比較輕鬆,所以如果想研究圖片框架的話,建議先從Picasso
下手,這樣會比較容易。
原始碼分析
今天只分析最簡單的一行程式碼,後面會慢慢深入。
雖然只有一行程式碼,但是裡面的整個邏輯確實非常複雜。
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(ivTest)
對,這應該也是我們使用Glide
時候最常用的一句程式碼,下面我們就一步步跟入。
- with
//------Glide.java------ //with過載方法有很多,這裡先講一個 public static RequestManager with(@NonNull FragmentActivity activity) { return getRetriever(activity).get(activity); } private static RequestManagerRetriever getRetriever(@Nullable Context context) { return Glide.get(context).getRequestManagerRetriever(); }
第一次觸發Glide.get
方法,預設建立一個Glide
物件,如下
public static Glide get(@NonNull Context context) { if (glide == null) { synchronized (Glide.class) { if (glide == null) { checkAndInitializeGlide(context); } } } return glide; } private static void checkAndInitializeGlide(@NonNull Context context) { isInitializing = true; initializeGlide(context); isInitializing = false; } private static void initializeGlide(@NonNull Context context) { initializeGlide(context, new GlideBuilder()); } //一步步往下看,就到了這裡,真正開始建立Glide物件的方法 private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) { .... //這裡程式碼很多,都省略,直接看最關鍵的一個方法,build Glide glide = builder.build(applicationContext); .... Glide.glide = glide; }
以上是查詢的過程的程式碼,不是特別重要,下面是GlideBuilder的build方法,非常重要。
Glide build(@NonNull Context context) { if (sourceExecutor == null) { sourceExecutor = GlideExecutor.newSourceExecutor(); } if (diskCacheExecutor == null) { diskCacheExecutor = GlideExecutor.newDiskCacheExecutor(); } if (animationExecutor == null) { animationExecutor = GlideExecutor.newAnimationExecutor(); } if (memorySizeCalculator == null) { memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); } if (connectivityMonitorFactory == null) { connectivityMonitorFactory = new DefaultConnectivityMonitorFactory(); } if (bitmapPool == null) { int size = memorySizeCalculator.getBitmapPoolSize(); if (size > 0) { bitmapPool = new LruBitmapPool(size); } else { bitmapPool = new BitmapPoolAdapter(); } } if (arrayPool == null) { arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes()); } if (memoryCache == null) { memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); } if (diskCacheFactory == null) { diskCacheFactory = new InternalCacheDiskCacheFactory(context); } if (engine == null) { engine = new Engine( memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor, GlideExecutor.newUnlimitedSourceExecutor(), GlideExecutor.newAnimationExecutor(), isActiveResourceRetentionAllowed); } RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory); return new Glide( context, engine, memoryCache, bitmapPool, arrayPool, requestManagerRetriever, connectivityMonitorFactory, logLevel, defaultRequestOptions.lock(), defaultTransitionOptions); }
Glide物件裡面存的東西很多,上面初始化了很多重要的東西,先不深入去看,但是根據名字也能大概的猜到物件的作用。
- sourceExecutor獲取源資料執行緒池
- diskCacheExecutor獲取diskcache執行緒池
- animationExecutor應該是跟動畫有關的執行緒池
- memorySizeCalculator 應該是個工具類,來計算記憶體的大小
- connectivityMonitorFactory 應該是連線監聽的一個工廠類
- bitmapPool 存放bitmap的池
- arrayPool 存放陣列的池
- memoryCache 資源快取物件
- diskCacheFactory disk cache工廠類
- requestManagerRetriever requestManager訓練器
大概就這樣一個初步的印象,可能不正確,後面可以慢慢驗證。
下面繼續回到剛才的with的方法。
@NonNull private static RequestManagerRetriever getRetriever(@Nullable Context context) { return Glide.get(context).getRequestManagerRetriever(); }
這裡的getRequestManagerRetriever,其實就是build方法中直接new出來的RequestManagerRetriever
public static RequestManager with(@NonNull FragmentActivity activity) { return getRetriever(activity).get(activity); }
所以我們再跟進去看下RequestManagerRetriever
的get
方法
@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 private RequestManager supportFragmentGet( @NonNull Context context, @NonNull FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) { //建立一個SupportRequestManagerFragment,來獲取生命週期的狀態,來對圖片進行管理(這個後面再深入,這裡可以簡單理解為,就是為了利用Fragment的生命週期) SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint, isParentVisible); RequestManager requestManager = current.getRequestManager(); //剛開始建立的SupportRequestManagerFragment的requestManager==null if (requestManager == null) { Glide glide = Glide.get(context); requestManager = factory.build( glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); current.setRequestManager(requestManager); } return requestManager; }
get
方法有很多過載,這裡我們就以引數為FragmentActivity
為例子。
get
方法主要是為了建立RequestManager
.
回過頭再看Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(ivTest)
,
接下來就是呼叫了RequestManager
物件的load
方法
public RequestBuilder<Drawable> load(@Nullable String string) { return asDrawable().load(string); } ... @NonNull @CheckResult public RequestBuilder<Drawable> asDrawable() { return as(Drawable.class); } ... //呼叫了as方法,其實主要就是建立一個RequestBuilder物件,然後傳入最終要轉換成的資源型別,顯然預設是轉換為Drawable.class @NonNull @CheckResult public <ResourceType> RequestBuilder<ResourceType> as( @NonNull Class<ResourceType> resourceClass) { return new RequestBuilder<>(glide, this, resourceClass, context); } ... @NonNull @Override @CheckResult public RequestBuilder<TranscodeType> load(@Nullable String string) { return loadGeneric(string); } ... //呼叫load方法傳入url地址時,並沒有真正的去發生請求獲取到圖片,只是設定了一個引數 @NonNull private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) { this.model = model; isModelSet = true; return this; }
這裡的程式碼其實比較簡單,沒有什麼好介紹的,簡單的說就是使用了建造者模式,然後建立了一個新的物件RequestBuilder
,然後傳入一些引數。既然是建造者模式,那麼最後RequestBuilder
肯定會生成一個Request
。
接下來再回頭看
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(ivTest)
下面就應該是呼叫RequestBuilder
的into
方法了
@NonNull public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) { ... return into( glideContext.buildImageViewTarget(view, transcodeClass), /*targetListener=*/ null, requestOptions); } ... private <Y extends Target<TranscodeType>> Y into( @NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, @NonNull RequestOptions options) { ... Request request = buildRequest(target, targetListener, options); //獲取到當前target繫結的request請求,如果現在正在執行的這個請求跟這個target之前繫結的請求是一樣的話, //就判斷下之前的請求是否有再執行,沒有執行就開始執行,有執行就不操作。並且回收當前要執行的Request物件 Request previous = target.getRequest(); if (request.isEquivalentTo(previous) && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { request.recycle(); ... if (!Preconditions.checkNotNull(previous).isRunning()) { ... previous.begin(); } return target; } requestManager.clear(target); //讓target跟request繫結 target.setRequest(request); //這裡才是正在發起請求的地方 requestManager.track(target, request); return target; }
into方法中有2句比較關鍵的地方,這裡提取出來單獨來講。
1. Request request = buildRequest(target, targetListener, options); 2. requestManager.track(target, request);
先看1,在RequestBuilder
中呼叫buildRequest,構建一個Request
物件
private Request buildRequest( Target<TranscodeType> target, @Nullable RequestListener<TranscodeType> targetListener, RequestOptions requestOptions) { return buildRequestRecursive( target, targetListener, /*parentCoordinator=*/ null, transitionOptions, requestOptions.getPriority(), requestOptions.getOverrideWidth(), requestOptions.getOverrideHeight(), requestOptions); ... private Request buildRequestRecursive( Target<TranscodeType> target, @Nullable RequestListener<TranscodeType> targetListener, @Nullable RequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, RequestOptions requestOptions) { ... Request mainRequest = buildThumbnailRequestRecursive( target, targetListener, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight, requestOptions); ... return errorRequestCoordinator; } ... //顧名思義,建立一個縮圖的Request,先判斷是否有設定縮放的一些熟悉 //如果沒有,就獲取一個沒有縮放的Request private Request buildThumbnailRequestRecursive( Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, @Nullable RequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, RequestOptions requestOptions) { if (thumbnailBuilder != null) { ... } else if (thumbSizeMultiplier != null) { ... Request thumbnailRequest = obtainRequest( target, targetListener, thumbnailOptions, coordinator, transitionOptions, getThumbnailPriority(priority), overrideWidth, overrideHeight); coordinator.setRequests(fullRequest, thumbnailRequest); return coordinator; } else { return obtainRequest( target, targetListener, requestOptions, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight); } } //繞了半天,這裡才是真正建立一個Request的地方 private Request obtainRequest( Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, RequestOptions requestOptions, RequestCoordinator requestCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight) { return SingleRequest.obtain( context, glideContext, model, transcodeClass, requestOptions, overrideWidth, overrideHeight, priority, target, targetListener, requestListeners, requestCoordinator, glideContext.getEngine(), transitionOptions.getTransitionFactory()); }
看一些優秀的三方原始碼,總是這樣,方法過載很多,方法引數很多,很容易暈,大家要一步步往裡面跟,總能看懂的。
這裡我們最後找到的是建立了一個SingleRequest
物件。當然如果說你在
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(ivTest)
設定了一些寬高,或者是縮放的屬性,那麼走的分支可能就不是這個。後面我們再分析。先從最簡單的分支入手,一步步解析。
接下來我們繼續看第2句關鍵的程式碼
requestManager.track(target, request);
void track(@NonNull Target<?> target, @NonNull Request request) { ... requestTracker.runRequest(request); } public void runRequest(@NonNull Request request) { requests.add(request); if (!isPaused) { //正常情況isPaused=false,走這個分支,開始請求 request.begin(); } else { request.clear(); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Paused, delaying request"); } pendingRequests.add(request); } } ... public void begin() { ... status = Status.WAITING_FOR_SIZE; //這裡先判斷overrideWidth,overrideHeight是否合法 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()); } ... }
我們這裡直接看到begin方法
private static boolean isValidDimension(int dimen) { return dimen > 0 || dimen == Target.SIZE_ORIGINAL; }
由於我們並沒有設定寬高,所以返回false,走下面分支
target.getSize(this); public void getSize(@NonNull SizeReadyCallback cb) { sizeDeterminer.getSize(cb); } void getSize(@NonNull SizeReadyCallback cb) { int currentWidth = getTargetWidth(); int currentHeight = getTargetHeight(); if (isViewStateAndSizeValid(currentWidth, currentHeight)) { cb.onSizeReady(currentWidth, currentHeight); return; } if (!cbs.contains(cb)) { cbs.add(cb); } if (layoutListener == null) { ViewTreeObserver observer = view.getViewTreeObserver(); layoutListener = new SizeDeterminerLayoutListener(this); observer.addOnPreDrawListener(layoutListener); } }
這裡比較關鍵的就是target.getSize(this);
方法中的引數this
,這裡的this
是一個SizeReadyCallback
.
而SingleRequest
實現了SizeReadyCallback
這裡就是等待layout佈局後,ImageView
有了width
和height後
就會進入SingleRequest
的onSizeReady
回撥方法。
主要的通過
ViewTreeObserver observer = view.getViewTreeObserver(); layoutListener = new SizeDeterminerLayoutListener(this); observer.addOnPreDrawListener(layoutListener);
這3句來監聽ImageView
的佈局之後的一個回撥,也就是有了width
和height
之後的回撥。
如果我們本身設定了縮放,或者是寬高屬性,那麼Glide就會直接使用width
和height
當作引數,呼叫
onSizeReady
.下面我們直接看。
public void onSizeReady(int width, int height) { ... if (status != Status.WAITING_FOR_SIZE) { return; } status = Status.RUNNING; float sizeMultiplier = requestOptions.getSizeMultiplier(); this.width = maybeApplySizeMultiplier(width, sizeMultiplier); this.height = maybeApplySizeMultiplier(height, sizeMultiplier); ... loadStatus = engine.load( glideContext, model, requestOptions.getSignature(), this.width, this.height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.isScaleOnlyOrNoTransform(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), requestOptions.getUseUnlimitedSourceGeneratorsPool(), requestOptions.getUseAnimationPool(), requestOptions.getOnlyRetrieveFromCache(), this); ... if (status != Status.RUNNING) { loadStatus = null; } ... }
這裡比較關鍵的地方
- 狀態的切換
- 呼叫engine.load
第1點就不說了,直接看上面程式碼就好。
直接看第2點engine.load
我們首先看下engine
這個物件是在哪裡初始化的。
private Request obtainRequest( Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, RequestOptions requestOptions, RequestCoordinator requestCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight) { return SingleRequest.obtain( context, glideContext, model, transcodeClass, requestOptions, overrideWidth, overrideHeight, priority, target, targetListener, requestListeners, requestCoordinator, glideContext.getEngine(), transitionOptions.getTransitionFactory()); }
可以看出來是前面構建SingleRequest
物件的時候glideContext.getEngine()
傳入的一個引數。
glideContext = new GlideContext( context, arrayPool, registry, imageViewTargetFactory, defaultRequestOptions, defaultTransitionOptions, engine, logLevel);
而glideContext中的engine
也是引數傳入的。
最終找到
@NonNull Glide build(@NonNull Context context) { ... if (engine == null) { engine = new Engine( memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor, GlideExecutor.newUnlimitedSourceExecutor(), GlideExecutor.newAnimationExecutor(), isActiveResourceRetentionAllowed); } ... return new Glide( context, engine, memoryCache, bitmapPool, arrayPool, requestManagerRetriever, connectivityMonitorFactory, logLevel, defaultRequestOptions.lock(), defaultTransitionOptions); }
是在建立Glide的時候new出來的一個Engine
,不自己傳入的話,會預設構建一個。然後供後面使用。
下面我們繼續看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, boolean isScaleOnlyOrNoTransform, Options options, boolean isMemoryCacheable, boolean useUnlimitedSourceExecutorPool, boolean useAnimationPool, boolean onlyRetrieveFromCache, ResourceCallback cb) { ... EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return null; } EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { cb.onResourceReady(cached, DataSource.MEMORY_CACHE); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; } EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null) { current.addCallback(cb); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); } EngineJob<R> engineJob = engineJobFactory.build( key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache); DecodeJob<R> decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb); engineJob.start(decodeJob); ... return new LoadStatus(cb, engineJob); }
程式碼很多,我們就看重點幾個地方。
先通過引數,生成key,通過key,去獲取快取資料,如果有快取就直接呼叫SingleRequest
的onResourceReady
方法。
//生成key,其實可以理解為就是一個字串,然後key-value,獲取到對應的快取 EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); //然後呼叫 loadFromActiveResources //然後呼叫 loadFromCache
如果快取中都沒有資料,那麼就繼續下面
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache); if (current != null) { current.addCallback(cb); if (VERBOSE_IS_LOGGABLE) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); }
先從jobs裡面通過key獲取前面已經加入的EngineJob。如果有,就直接current.addCallback(cb);
。
意思就是說,前面如果已經執行過一個任務了,就會把任務新增到jobs
,如果後面遇到相同的任務了,就直接在jobs裡面獲取,可以把jobs就認為是一個HashMap,根據key
來儲存。
如果說,是第一次執行任務,也就是載入圖片,那麼current==null,繼續往下走。
EngineJob<R> engineJob = engineJobFactory.build( key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache); DecodeJob<R> decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb); engineJob.start(decodeJob); ... return new LoadStatus(cb, engineJob);
建立2個物件,EngineJob和DecodeJob。
jobs.put(key, engineJob);
這裡是為了後面如果是載入相同的圖片的話,這裡會直接獲取到EngineJob
然後去處理,而不是每次都新建一個。
接下來繼續engineJob.start(decodeJob);
public void start(DecodeJob<R> decodeJob) { this.decodeJob = decodeJob; GlideExecutor executor = decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor(); executor.execute(decodeJob); } ... boolean willDecodeFromCache() { Stage firstStage = getNextStage(Stage.INITIALIZE); return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE; }
這裡判斷了下是使用diskCacheExecutor
還是getActiveSourceExecutor()
,其實第一次看原始碼的時候,我們可以先來走一遍完整的流程,這2者具體的區別我們先不要太在意。
這2者其實都是一個ExecutorService
,是用來處理執行緒的。
那我們繼續。
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback, Runnable, Comparable<DecodeJob<?>>, Poolable
DecodeJob實現了Runnable介面
這裡呼叫
executor.execute(decodeJob);
其實就是線上程池中找一個執行緒來執行decodeJob中的run方法。
@Override public void run() { ... DataFetcher<?> localFetcher = currentFetcher; try { if (isCancelled) { notifyFailed(); return; } runWrapped(); } catch (Throwable t) { ... } } //通過狀態來獲取不同的生成器,來生成資源 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 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: // Skip loading from source if the user opted to only retrieve the resource from cache. return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE; case SOURCE: case FINISHED: return Stage.FINISHED; default: throw new IllegalArgumentException("Unrecognized stage: " + current); } }
上面最重要的方法就是runWrappeed
。
主要3點。
- 獲取當前狀態的下一個狀態,然後賦值給當前狀態
- 獲取與當前匹配的Generator
- 執行生成器來獲取資源。
我們先來看第一點,也就是getNextStage
方法,通過當前狀態,來獲取後面的一個狀態。其實很簡單。
狀態的順序就是
INITIALIZE(初始化)-> RESOURCE_CACHE(獲取記憶體快取)-> DATA_CACHE(獲取磁碟快取)-> SOURCE(真正去請求資源)-> FINISHED(完成)
正常情況下,就會按這樣步驟一個個來,但是有些時候我們會設定不快取的一些引數,那麼就會跳過某個步驟。
在程式碼中也有體現
case INITIALIZE: return diskCacheStrategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
初始化後應該是從獲取資源快取,但是diskCacheStrategy.decodeCachedResource()
返回false
的話,那麼就直接獲取getNextStage(Stage.RESOURCE_CACHE)
,也就是資源快取的下一個狀態。
DiskCacheStrategy
代表快取的策略,一共有
- DiskCacheStrategy.ALL
- DiskCacheStrategy.NONE
- DiskCacheStrategy.DATA
- DiskCacheStrategy.RESOURCE
- DiskCacheStrategy.AUTOMATIC
我們這裡來看下DiskCacheStrategy.NONE
public static final DiskCacheStrategy NONE = new DiskCacheStrategy() { public boolean isDataCacheable(DataSource dataSource) { return false; } public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource, EncodeStrategy encodeStrategy) { return false; } public boolean decodeCachedResource() { return false; } public boolean decodeCachedData() { return false; } };
這裡就都返回false。不允許獲取快取資料。
下面來看第2點,獲取Generator
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); } }
通過不同的stage獲取到不同的Generator
。一開始獲取到ResourceCacheGenerator
。
下面看第3點,runGenerators
private void runGenerators() { ... while (!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) { stage = getNextStage(stage); currentGenerator = getNextGenerator(); if (stage == Stage.SOURCE) { reschedule(); return; } } }
這裡程式碼很簡單,取消的情況先不考慮,主要是這句程式碼
!(isStarted = currentGenerator.startNext())
Generator
中註冊了很多ModelLoader<File, ?>
,ModelLoader<File, ?>
可以生成對應的處理資源的LoadData
,
不同的LoadData
只能載入自己能載入到的資源。
public boolean startNext() { ... 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; }
最終呼叫的是LoadData
的fetcher
的loadData方法。
這裡的fetcher
是HttpUrlFetcher
(為什麼是這個後面可以再深入講,先走完整個流程)。
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) { ... InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); callback.onDataReady(result); ... }
loadDataWithRedirects
這個方法就不深入介紹了,使用了HttpURLConnection
去請求資源。
請求完成後,呼叫了onDataReady
方法,把結果往上傳。
這裡我們就要一步步往上找到回撥方法。
首先剛才在SourceGenerator
呼叫的
loadData.fetcher.loadData(helper.getPriority(), this);
會發現,引數DataCallback就是SourceGenerator
。
所以回撥的其實是SourceGenerator
的onDataReady
方法
@Override public void onDataReady(Object data) { ... dataToCache = data; cb.reschedule(); ... }
這裡又有一個cb也就是Callback
,繼續往前找,發現是DecodeJob
,
所以這裡又呼叫了,DecodeJob.reschedule();
public void reschedule() { runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; callback.reschedule(this); }
DecodeJob
中又呼叫了callback.reschedule
,其實這裡的callback
是EngineJob
。很多優秀的三方庫就是這樣,繞來繞去的,看起來比較費勁。
@Override public void reschedule(DecodeJob<?> job) { getActiveSourceExecutor().execute(job); }
這裡會發現一個很奇怪的東西,因為在前面我們已經介紹過了,入口就是執行DecodeJob
的run
方法,然後執行完成之後一步步回撥,這裡竟然又去執行DecodeJob
的run
,死迴圈麼,當然不是,我們繼續往下看。
之後的流程跟前面都一樣,這裡就不再贅述了,然後又到了SourceGenerator
的startNext
方法
public boolean startNext() { if (dataToCache != null) { Object data = dataToCache; dataToCache = null; cacheData(data); } if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { return true; } .. return started; } ... private void cacheData(Object dataToCache) { ... sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this); }
這個時候跟之前就不一樣了,因為資料請求已經回來了,dataToCache!=null
,然後呼叫cacheData
方法,把資料快取起來。
呼叫cacheData
方法之後,最後建立了一個DataCacheGenerator
。然後呼叫startNext
方法。
public boolean startNext() { while (modelLoaders == null || !hasNextModelLoader()) { sourceIdIndex++; if (sourceIdIndex >= cacheKeys.size()) { return false; } Key sourceId = cacheKeys.get(sourceIdIndex); @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") 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; while (!started && hasNextModelLoader()) { ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++); loadData = modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions()); if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) { started = true; loadData.fetcher.loadData(helper.getPriority(), this); } } return started; }
這裡的程式碼呢,其實跟前面的SourceGenrator
差不多,由於前面已經快取了資料,所以cacheFile!=null,獲取到的modelLoader
其實是ByteBufferFileLoader
,然後fetcher
是ByteBufferFetcher
,所以
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super ByteBuffer> callback) { ByteBuffer result; try { result = ByteBufferUtil.fromFile(file); } catch (IOException e) { ... callback.onLoadFailed(e); return; } callback.onDataReady(result); }
直接載入檔案,返回二進位制陣列,然後呼叫回撥函式。這裡的callback
其實就是DataCacheGenerator
,跟前面一樣的,就是不停往前面找。仔細點,還是很簡單的。
@Override public void onDataReady(Object data) { cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA_DISK_CACHE, sourceKey); }
這裡cb是DecodeJob
。
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) { ... runReason = RunReason.DECODE_DATA; callback.reschedule(this); ...
又開始了,設定了一下值runReason = RunReason.DECODE_DATA,又呼叫了callback
也就是EngineJob
的reschedule
方法。
這裡我就不繼續往前找了,最後還是呼叫了DecodeJob
的run
方法
public void run() { ... runWrapped(); ... } private void runWrapped() { switch (runReason) { ... case DECODE_DATA: decodeFromRetrievedData(); break; ... } } private void decodeFromRetrievedData() { ... Resource<R> resource = null; try { resource = decodeFromData(currentFetcher, currentData, currentDataSource); ... if (resource != null) { notifyEncodeAndRelease(resource, currentDataSource); } else { runGenerators(); } } private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) { .... notifyComplete(result, dataSource); ... } private void notifyComplete(Resource<R> resource, DataSource dataSource) { ... callback.onResourceReady(resource, dataSource); }
這裡就比較簡單了,獲取到資源然後解碼後,呼叫callback也就是EngineJob
的onResourceReady
方法
public void onResourceReady(Resource<R> resource, DataSource dataSource) { ... MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget(); } public boolean handleMessage(Message message) { ... switch (message.what) { case MSG_COMPLETE: job.handleResultOnMainThread(); ... } void handleResultOnMainThread() { ... cb.onResourceReady(engineResource, dataSource); ... }
這裡的cb其實就是SingleRequest
了。
public void onResourceReady(Resource<?> resource, DataSource dataSource) { ... onResourceReady((Resource<R>) resource, (R) received, dataSource); } private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) { ... target.onResourceReady(result, animation); ... }
這裡的target
其實就是ImageViewTarget
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) { ... setResourceInternal(resource); ... } private void setResourceInternal(@Nullable Z resource) { ... setResource(resource); ... } protected abstract void setResource(@Nullable Z resource);
可以看出setResource
是一個抽象方法。
Drawable.class
所以這裡的實現是DrawableImageViewTarget
protected void setResource(@Nullable Drawable resource) { view.setImageDrawable(resource); }
這裡的view其實就是我們的ImageView
到這裡,最簡單的Glide載入網路圖片的流程已經走完。
如果對整個流程不懂的同學,其實是可以debug一下,然後一步一步跟進去。但是由於流程跳來跳去的,可能斷點不是很好打。
大家可以先看下我的整個流程,瞭解大概之後就可以自己打斷點了。
後續還會繼續深入Glide
原始碼。有興趣的同學可以關注下。