glide現在已經4.x了, 就以該版本的源碼進行分析了.
glide優勢
- 通過Fragment來監聽頁面生命週期來控制圖片的加載與取消;
- 使用@GlideModel的方式可動態註冊、替換圖片加載器;
- glide初始化時註冊加載器、轉碼器、編碼器等;
- Target通過ViewTreeObserver來監聽控件的寬高提供給BitmapFactory.decodeStream()來解碼原始流;
- 四級緩存, 活躍緩存、內存緩存、硬盤緩存、網絡加載;
- 可以返回多種數據格式Drawable、Bitmap.
Glide.with(this)
initializeGlide()
解碼資源的接口(如將File, InputStream etc 解碼爲Bitmap, Drawable etc): public interface ResourceDecoder<T, Z> ;
將數據編碼寫入持久性數據存儲區(如本地文件緩存)的接口: public interface Encoder<T> ;
將一種類型的資源轉碼爲另一種類型的資源(如Resources轉BitmapDrawable): public interface ResourceTranscoder<Z, R> ;
將任意複雜的數據模型轉換爲具體的數據類型的工廠接口(如請求網絡獲取原始流): public interface ModelLoader<Model, Data>;
- encoderRegistry.append(dataClass, encoder); 數據編碼存儲註冊
- decoderRegistry.append(bucket/**解碼器的存儲桶標識符*/, decoder/**已註冊的ResourceDecoder*/, dataClass/**被解碼的數據(比如流)*/, resourceClass/**解碼結果數據(比如Bitmap)*/); 解碼器註冊
- modelLoaderRegistry.append(modelClass/**模型類(如URL, file path)*/, dataClass/**返回的數據類型(如InputStream)*/, factory/**模型加載工廠類*/); 模型加載器註冊
- transcoderRegistry.register(resourceClass/**將被轉碼的類型(如Bitmap)*/, transcodeClass/**轉碼後的類型(如BitmapDrawable)*/, transcoder/**已註冊的資源轉碼器*/); 轉碼器註冊
- RequestManagerRetriever.ID_REMOVE_SUPPORT_FRAGMENT_MANAGER 存在的作用???
- RequestManagerRetriever.supportFragmentGet() 中實現將RequestManager註冊到SupportRequestManagerFragment的ActivityFragmentLifecycle屬性中來感知Fragment的生命週期來進行圖片的加載與取消.
- 反射獲取業務方@GlideModel註解的類;
- manifest解析
- 排除manifest下被註解配置的model
- 引用傳遞參數給自定義模塊
RequestManager.load(url)
/**
* 指定ResourceType爲Drawable, transcodeClass爲Drawable.class
*/
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
/**
* 結合上面方法:load(url)後 model類型爲String, transcodeClass爲Drawable.class
*/
public RequestBuilder<Drawable> load(@Nullable String string) {
return asDrawable().load(string);
}
生命週期綁定
傳入的不同類型的參數Fragment、Activity、Context、Application創建一個沒有界面的 RequestManagerFragment , 返回 RequestManager 對象, 爲了綁定生命週期將 RequestManagerFragment 的屬性 ActivityFragmentLifecycle 傳遞到 RequestManager, 讓 RequestManager 將自己註冊到 ActivityFragmentLifecycle 的觀察者集合, 從而在 RequestManagerFragment 的生命週期方法中通知事件訂閱者. 構造 RequestManager 對象時初始化了 RequestManager.glide 屬性 , 此時 glide的屬性, 註冊不同格式數據源的編碼、解碼器對象, 不同數據來源的加載器對象, 圖片對象變換器, 編碼轉換器等被初始化.
資源加載器、轉換器、編碼器、變換器、內存緩存、硬盤緩存、active緩存對象的初始化
// glide對象的屬性
private Engine engine;
private BitmapPool bitmapPool;
private MemoryCache memoryCache; // LruResourceCache 原數據內存緩存
private ExecutorService sourceService; // FIFO 網絡請求線程池
private ExecutorService diskCacheService; // FIFO 磁盤操作線程池
private DecodeFormat decodeFormat;
private DiskCache.Factory diskCacheFactory; // 磁盤操作接口類DiskCache
// 爲 Engine 類的屬性, 構造glide對象時初始化glide.engine屬性
private final Map<Key, EngineJob> jobs;
private final EngineKeyFactory keyFactory;
private final MemoryCache cache;
private final EngineJobFactory engineJobFactory;
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
private final ResourceRecycler resourceRecycler;
private final LazyDiskCacheProvider diskCacheProvider;
RequestManager.load(url)
根據as(Drawable.class)參數類型Drawable.class構建RequestBuilder(Glide glide, RequestManager requestManager, Class<TranscodeType> transcodeClass, Context context)對象, 繼續構造this.mode = url; 返回RequestBuilder對象.
RequestBuilder.into(imageView)
轉換爲
RequestBuilder.into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
其中glideContext是在Glide的構造方法中初始化的, 使用工廠模式根據參數的classType從ImageViewTargetFactory構造一個(ViewTarget<ImageView, Z>)DrawableImageViewTarget對象, 請求類SingleRequest, 請求池類 Pools.Pool<SingleRequest<?>>, 在調用engine.load()傳入了this作爲回調接口ResourceCallback,
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
執行網絡請求的地方
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
真正的加載是在Engine.load()中的DecodeJob.run();
public <R> LoadStatus load(...) {
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);
return null;
}
// 內存緩存
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob = engineJobFactory.build(...);
DecodeJob<R> decodeJob = decodeJobFactory.build(..., engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
}
runWrapped()
private void runWrapped() {
switch (runReason) { // 初始化爲INITIALIZE
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE); // 遞歸獲取值爲 Stage.SOURCE
currentGenerator = getNextGenerator(); // 返回 new SourceGenerator(decodeHelper, this);
runGenerators(); /** 執行SourceGenerator.startNext()下載網絡圖片 */
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);
}
}
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);
}
}
加載數據的入口
使用OkHttp加載數據並回調
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
DataSource dataSource, Key attemptedKey) {
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
解碼和轉碼
private void decodeFromRetrievedData() {
Resource<R> resource = null;
try {
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
從註冊的解碼器集合中查找能處理原數據的解碼器
解碼
解碼變換完成切換線程
String類型的url默認是HttpUrlFetcher.loadData()加載圖片, 回調是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);
}
}
從網絡加載圖片數據OkHttpStreamFetcher
硬盤緩存加載時序圖
測試的時候硬盤緩存策略使用的是DiskCacheStrategy.DATA
public static final DiskCacheStrategy DATA = new DiskCacheStrategy() {
@Override
public boolean isDataCacheable(DataSource dataSource) {
return dataSource != DataSource.DATA_DISK_CACHE && dataSource != DataSource.MEMORY_CACHE;
}
@Override
public boolean isResourceCacheable(boolean isFromAlternateCacheKey, DataSource dataSource,
EncodeStrategy encodeStrategy) {
return false;
}
@Override
public boolean decodeCachedResource() {
return false;
}
@Override
public boolean decodeCachedData() {
return true;
}
};
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);
}
}
解轉碼流程
緩存相關類
硬盤緩存相關類: DiskLruCacheFactory、 DiskLruCacheWrapper、InternalCacheDiskCacheFactory
硬盤緩存讀取 DiskLruCacheWrapper.get() 的調用方:
DataCacheGenerator.startNext() 嘗試先從磁盤緩存中讀取, 未獲取到再從網絡讀取;
ResourceCacheGenerator.startNext() 嘗試先從磁盤緩存中讀取, 未獲取到再從網絡讀取.
硬盤緩存存儲 DiskLruCacheWrapper.put() 的調用方:
DecodeJo$DeferredEncodeManager.encode()存儲到硬盤
內存緩存相關類: LruResourceCache、ActiveResources
ActiveResources.activate()的調用方:
Engine.load() -> Engine.loadFromCache() ;
Engine.onEngineJobComplete().
ActiveResources.get()的調用方:
Engine.load() -> Engine.loadFromActiveResources()
ActiveResources.deactivate()的調用方:
Engine.onResourceReleased()
LruResourceCache.remove()的調用方:
Engine.load() -> Engine.loadFromCache() -> Engine.getEngineResourceFromCache()
LruResourceCache.put()的調用方:
Engine.onResourceReleased();
Engine.cache.setResourceRemovedListener(Engine.this);
BitmapPool相關類: LruBitmapPool
變化工具類: TransformationUtils
SimpleTarget的使用追蹤
DownsampleStrategy內置了幾個DownsampleStrategy.CenterOutside
// sourceWidth 服務器端返回的圖片寬度
// requestedWidth UI控件的寬度
// 返回縮放百分比的較大值
public float getScaleFactor(int sourceWidth, int sourceHeight, int requestedWidth,
int requestedHeight) {
float widthPercentage = requestedWidth / (float) sourceWidth;
float heightPercentage = requestedHeight / (float) sourceHeight;
return Math.max(widthPercentage, heightPercentage);
}
關鍵問題代碼
int targetWidth = requestedWidth == Target.SIZE_ORIGINAL ? sourceWidth : requestedWidth;
int targetHeight = requestedHeight == Target.SIZE_ORIGINAL ? sourceHeight : requestedHeight;
具體的縮放因子與ImageView控件的scaleType有關, 參考類DownsampleStrategy
options.inTargetDensity > 0 && options.inDensity
Glide.with(MainActivity.this)
.load(url)
.into(imageView);
分析一下調用後此處圖片的寬高和控件的寬高:
RequestBuilder.into()中通過工廠類ImageViewTargetFactory.buildTarget()獲取DrawableImageViewTarget對象;
執行SingleRequest.begin()的
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
調用的是ViewTarget.getSize(), 其實是調用的ViewTarget$SizeDeterminer.getSize(),核心是
if (layoutListener == null) {
ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnPreDrawListener(layoutListener);
}
private static final class SizeDeterminerLayoutListener
implements ViewTreeObserver.OnPreDrawListener {
private final WeakReference<SizeDeterminer> sizeDeterminerRef;
SizeDeterminerLayoutListener(@NonNull SizeDeterminer sizeDeterminer) {
sizeDeterminerRef = new WeakReference<>(sizeDeterminer);
}
@Override
public boolean onPreDraw() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "OnGlobalLayoutListener called attachStateListener=" + this);
}
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
sizeDeterminer.checkCurrentDimens();
}
return true;
}
}
再回到SingleRequest.onSizeReady()繼續執行. 到執行InputStream解碼的時候, 會根據控件的scaleType、寬高和返回的圖片流數據中的寬高做縮放, 使用了Option.inSample屬性.
下次分析:
每次圖片加載都new 一個Engine?
參考文檔
官網介紹: http://bumptech.github.io/glide/doc/transformations.html
詳細blog: https://blog.csdn.net/yanfeivip8/article/details/50418064
郭霖blog: https://blog.csdn.net/guolin_blog/article/details/53759439