一、客户端代码介绍
这里分两个部分:
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> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
List> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader
这里modelLoaders是在Registry中由ModelLoaderRegistry来获取所有的models。这里model对应ByteBufferFileLoader,由他执行buildLoadData。
ByteBufferFileLoader.java
@Override
public LoadData 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 {
public final Key sourceKey;
public final List alternateKeys;
public final DataFetcher fetcher;
...
}
那么总结一下:首先是获取对应数据源类型的ModelLoader,ModelLoader初始化一个LoadData,然后LoadData通过内部关联的DataFetcher来正真去执行加载数据的操作!
这里很显然对应的DataFetcher实例是LoadData初始化时传入的ByteBufferFetcher。回到SourceGenerator的startNext方法,最终调用ByteBufferFetcher的loadData。
当然这里数据获取的方式有很多种,有网络请求、有磁盘文件获取等等:
这里DataFetcher也可以自定义,举例:
Glide网络请求默认使用的是HttpUrlConnection,这里可以替换为Okhttp请求。
做法是:
public class OkHttpUrlLoader implements ModelLoader {
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 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 Resource decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath path = decodeHelper.getLoadPath((Class) data.getClass());
return runLoadPath(data, dataSource, path);
}
DecodeHelper.java
LoadPath getLoadPath(Class dataClass) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
这里很显然又是去Registry拿的。如果有的话最终会返回一个LoadPath对象
new LoadPath<>(dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
继续往下走:
private Resource runLoadPath(Data data, DataSource dataSource,
LoadPath path) throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
DataRewinder 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(dataSource));
} finally {
rewinder.cleanup();
}
}
这里调用LoadPath的load方法,该方法调用loadWithExceptionList
LoadPath.java
private Resource loadWithExceptionList(DataRewinder rewinder,
@NonNull Options options,
int width, int height, DecodePath.DecodeCallback decodeCallback,
List exceptions) throws GlideException {
Resource result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath 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动图资源。
整个数据转换流程为: