一、客戶端代碼介紹
這里分兩個部分:
1)添加webp動圖解碼組件
@GlideModule
public class WebpGlideLibraryModule extends LibraryGlideModule {
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
final BitmapPool bitmapPool = glide.getBitmapPool();
final ArrayPool arrayPool = glide.getArrayPool();
/* animate webp decoders */
ByteBufferWebpDecoder byteBufferWebpDecoder = new ByteBufferWebpDecoder(context, arrayPool, bitmapPool);
...
/* Animated webp images */
registry.prepend(InputStream.class, WebpDrawable.class, new StreamWebpDecoder(byteBufferWebpDecoder, arrayPool))
...
}
}
ByteBufferWebpDecoder是最終webp動圖資源解碼器
2)Glide加載webp動圖url
Glide.with(mContext).load(Constants.dynamicWebpUrl).into(mImageView);
二、Glide加載網絡webp數據轉換流程
先給出Glide加載webp動圖的完整調用棧:
com.bumptech.glide.integration.webp.decoder.ByteBufferWebpDecoder.decode:33
com.bumptech.glide.load.engine.DecodePath.decodeResourceWithList:72
com.bumptech.glide.load.engine.DecodePath.decodeResource:55
com.bumptech.glide.load.engine.DecodePath.decode:45
com.bumptech.glide.load.engine.LoadPath.loadWithExceptionList:62
com.bumptech.glide.load.engine.LoadPath.load:47
com.bumptech.glide.load.engine.DecodeJob.runLoadPath:510
com.bumptech.glide.load.engine.DecodeJob.decodeFromFetcher:475
com.bumptech.glide.load.engine.DecodeJob.decodeFromData:461
com.bumptech.glide.load.engine.DecodeJob.decodeFromRetrievedData:413
com.bumptech.glide.load.engine.DecodeJob.onDataFetcherReady:382
com.bumptech.glide.load.engine.SourceGenerator.onDataFetcherReady:139
com.bumptech.glide.load.engine.DataCacheGenerator.onDataReady:99
com.bumptech.glide.load.model.ByteBufferFileLoader$ByteBufferFetcher.loadData:74
com.bumptech.glide.load.engine.DataCacheGenerator.startNext:79
com.bumptech.glide.load.engine.SourceGenerator.startNext:53
com.bumptech.glide.load.engine.DecodeJob.runGenerators:305
com.bumptech.glide.load.engine.DecodeJob.runWrapped:275
com.bumptech.glide.load.engine.DecodeJob.run:236
java.util.concurrent.ThreadPoolExecutor.runWorker:1167
java.util.concurrent.ThreadPoolExecutor$Worker.run:641
com.bumptech.glide.load.engine.EngineJob.start:118
com.bumptech.glide.load.engine.Engine.load:233
com.bumptech.glide.request.SingleRequest.onSizeReady:432
com.bumptech.glide.request.target.ViewTarget$SizeDeterminer.getSize:389
com.bumptech.glide.request.target.ViewTarget.getSize:221
com.bumptech.glide.request.SingleRequest.begin:257
com.bumptech.glide.manager.RequestTracker.runRequest:44
com.bumptech.glide.RequestManager.track:616
com.bumptech.glide.RequestBuilder.into:651
com.bumptech.glide.RequestBuilder.into:711
整個流程主要分三塊:
- 封裝并發起一個圖片加載Request
- 圖片資源獲取:內存緩存、磁盤文件緩存、網絡請求等。
- 圖片資源解析:對某些數據源進行資源解碼。
2.1 封裝并發起一個圖片加載Request
load :通過RequestManager加載一個String 類型的model。
into:加載一個ImageView的目標控件作為target,然后通過RequestBuilder開始數據處理流程。
2.2 圖片資源獲取
EngineJob以前的流程非常簡單明確,這里著重看下DecodeJob部分的處理流程:
SourceGenerator.java
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
這里有個ModelLoader-LoadData-DataFetcher關系需要捋一下:
DecodeHelper.java
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); 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;
}
這里modelLoaders是在Registry中由ModelLoaderRegistry來獲取所有的models。這里model對應ByteBufferFileLoader,由他執行buildLoadData。
ByteBufferFileLoader.java
@Override
public LoadData<ByteBuffer> buildLoadData(@NonNull File file, int width, int height,
@NonNull Options options) {
return new LoadData<>(new ObjectKey(file), new ByteBufferFetcher(file));
}
而LoadData是ModelLoader的內部類,它的屬性包括一個DataFetcher,它就是最終加載數據的地方。
class LoadData<Data> {
public final Key sourceKey;
public final List<Key> alternateKeys;
public final DataFetcher<Data> fetcher;
...
}
那么總結一下:首先是獲取對應數據源類型的ModelLoader,ModelLoader初始化一個LoadData,然后LoadData通過內部關聯的DataFetcher來正真去執行加載數據的操作!
這里很顯然對應的DataFetcher實例是LoadData初始化時傳入的ByteBufferFetcher。回到SourceGenerator的startNext方法,最終調用ByteBufferFetcher的loadData。
當然這里數據獲取的方式有很多種,有網絡請求、有磁盤文件獲取等等:
這里DataFetcher也可以自定義,舉例:
Glide網絡請求默認使用的是HttpUrlConnection,這里可以替換為Okhttp請求。
做法是:
public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private final Call.Factory client;
// Public API.
@SuppressWarnings("WeakerAccess")
public OkHttpUrlLoader(@NonNull Call.Factory client) {
this.client = client;
}
@Override
public boolean handles(@NonNull GlideUrl url) {
return true;
}
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
@NonNull Options options) {
return new LoadData<>(model, new OkHttpStreamFetcher(client, model));
}
...
}
創建對應的ModelLoader,并且自定義一個OkHttpStreamFetcher來實現Okhttp網絡請求功能,同時通過Registry去替換組件:
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
這里基本上圖片資源獲取就介紹完了。
2.3 圖片資源解析
資源獲取成功后,會通過callback.onDataReady(result)進行回調,這個callback是通過參數傳入的loadData.fetcher.loadData(helper.getPriority(), this),這里的this就是SourceGenerator
SourceGenerator.java
@Override
public void onDataReady(Object data) {
DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
dataToCache = data;
// We might be being called back on someone else's thread. Before doing anything, we should
// reschedule to get back onto Glide's thread.
cb.reschedule();
} else {
cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
loadData.fetcher.getDataSource(), originalKey);
}
}
這個cb是FetcherReadyCallback,在SourceGenerator初始化時傳入
SourceGenerator(DecodeHelper<?> helper, FetcherReadyCallback cb) {
this.helper = helper;
this.cb = cb;
}
從SourceGenerator初始化出追這個cb,就是DecodeJob,這樣最終獲取的數據源通過兩層callback傳入了DecodeJob,準備進行解碼處理。
DecodeJob.java
onDataFetcherReady() -> decodeFromRetrievedData() ->decodeFromData()->decodeFromFetcher()
這個流程沒什么可分析的,直接到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);
}
DecodeHelper.java
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data> dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
這里很顯然又是去Registry拿的。如果有的話最終會返回一個LoadPath對象
new LoadPath<>(dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
繼續往下走:
private <Data, ResourceType> Resource<R> runLoadPath(Data data, DataSource dataSource,
LoadPath<Data, ResourceType, R> path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
return path.load(
rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
} finally {
rewinder.cleanup();
}
}
這里調用LoadPath的load方法,該方法調用loadWithExceptionList
LoadPath.java
private Resource<Transcode> loadWithExceptionList(DataRewinder<Data> rewinder,
@NonNull Options options,
int width, int height, DecodePath.DecodeCallback<ResourceType> decodeCallback,
List<Throwable> exceptions) throws GlideException {
Resource<Transcode> result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
try {
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
這里獲取了一個decodePath,然后調用它的decode方法去執行具體的解碼工作了。debug一個看看這里path是什么
這里就是文章開頭說的客戶端自定義添加webp動圖解碼組件。具體解碼處理留到下一篇分析。
好的,最后再來簡單總結下整個流程:
上層Glide作為客戶端調用的主入口,通過RequestManager以及RequestBuilder收集圖片源model以及目標控件target,創建一個對應的request交給EngineJob線程池去處理這個request,DecodeJob作為一個執行線程接收這個request任務,然后交由Generator選擇處理方式,包括緩存還是網絡請求等,而它通過獲取Registry注冊的對應的ModelLoader-LoadData-DataFetcher最終去獲取圖片數據。然后由Generator回調給DecodeJob,DecodeJob通過向Registry獲取對應的LoadPath,最終匹配到對應的解碼器DecodePath去執行解碼操作。
后面就是將通過onSourceReady層層回調返回到SingleRequest,最終為目標控件設置webp動圖資源。
整個數據轉換流程為: