Glide源码解析四(解码和转码)
本文基于Glide 4.11.0
Glide加载过程有一个解码过程,比如将url加载为inputStream后,要将inputStream解码为Bitmap。
从
Glide源码解析一
我们大致知道了Glide加载的过程,所以我们可以直接从这里看起,在这个过程中我们以从文件中加载bitmap为例:
DecodeJob的一个方法:
private voiddecodeFromRetrievedData() {if(Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Retrieved data", startFetchTime,"data: " +currentData+ ", cache key: " +currentSourceKey+ ", fetcher: " +currentFetcher);
}
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();
}
}
主要是这个方法:resource = decodeFromData(currentFetcher, currentData, currentDataSource);
这时候currentData为FileInputStream,因为我们加载的是本地文件。
currentDateSource为LOCAL,即为本地的资源
我们继续找下去
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
----------------->
Resource<R> result = decodeFromFetcher(data, dataSource);
------------------>
private <Data> Resource<R>decodeFromFetcher(Data data, DataSource dataSource)throwsGlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());return runLoadPath(data, dataSource, path);
这里获取到LoadPath的对象,我么先看看LoadPath有什么?
我们可以看到一个DecodePaths:
DecodePath里面又保存着decoders
decoders便是我们需要的解码器,拿到解码器后就可以进行解码了。
那怎么拿到?
在
Glide源码解析三
中我们知道这些解码器都注册在Register中,所以我们也是要通过它来拿:
<Data> LoadPath<Data, ?, Transcode> getLoadPath(Class<Data>dataClass) {returnglideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
---------------->
@Nullablepublic <Data, TResource, Transcode> LoadPath<Data, TResource, Transcode>getLoadPath(
@NonNull Class<Data> dataClass, @NonNull Class<TResource>resourceClass,
@NonNull Class<Transcode>transcodeClass) {
LoadPath<Data, TResource, Transcode> result =loadPathCache.get(dataClass, resourceClass, transcodeClass);if(loadPathCache.isEmptyLoadPath(result)) {return null;
}else if (result == null) {
List<DecodePath<Data, TResource, Transcode>> decodePaths =getDecodePaths(dataClass, resourceClass, transcodeClass);//It's possible there is no way to decode or transcode to the desired types from a given//data class. if(decodePaths.isEmpty()) {
result= null;
}else{
result= new LoadPath<>(
dataClass, resourceClass, transcodeClass, decodePaths, throwableListPool);
}
loadPathCache.put(dataClass, resourceClass, transcodeClass, result);
}returnresult;
}
首先会先从缓存中拿,缓存中拿不到再通过下面的方法去拿:
List<DecodePath<Data, TResource, Transcode>> decodePaths = getDecodePaths(dataClass, resourceClass, transcodeClass);
private <Data, TResource, Transcode> List<DecodePath<Data, TResource, Transcode>>getDecodePaths(
@NonNull Class<Data> dataClass, @NonNull Class<TResource>resourceClass,
@NonNull Class<Transcode>transcodeClass) {
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
List<Class<TResource>> registeredResourceClasses =decoderRegistry.getResourceClasses(dataClass, resourceClass);for (Class<TResource>registeredResourceClass : registeredResourceClasses) {
List<Class<Transcode>> registeredTranscodeClasses =transcoderRegistry.getTranscodeClasses(registeredResourceClass, transcodeClass);for (Class<Transcode>registeredTranscodeClass : registeredTranscodeClasses) {
List<ResourceDecoder<Data, TResource>> decoders =decoderRegistry.getDecoders(dataClass, registeredResourceClass);
ResourceTranscoder<TResource, Transcode> transcoder =transcoderRegistry.get(registeredResourceClass, registeredTranscodeClass);
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
DecodePath<Data, TResource, Transcode> path = new DecodePath<>(dataClass, registeredResourceClass, registeredTranscodeClass,
decoders, transcoder, throwableListPool);
decodePaths.add(path);
}
}returndecodePaths;
}
该方法各个参数如下:
dataClass为InputStream
,这是被解码的对象
resourceClass为Object
,要解码成为Object
transcodeClass为Drawable
,要转码为Drawable
我们看这个方法:
decoderRegistry.getResourceClasses:
public synchronized <T, R> List<Class<R>> getResourceClasses(@NonNull Class<T>dataClass,
@NonNull Class<R>resourceClass) {
List<Class<R>> result = new ArrayList<>();for(String bucket : bucketPriorityList) {
List<Entry<?, ?>> entries =decoders.get(bucket);if (entries == null) {continue;
}for (Entry<?, ?>entry : entries) {if(entry.handles(dataClass, resourceClass)&& !result.contains((Class<R>) entry.resourceClass)) {
result.add((Class<R>) entry.resourceClass);
}
}
}returnresult;
}
该方法是为了获取解码器中的resourceClass,即解码后的资源类型。
我们可以看到decoder这个map里面的内容:
各种类型对应的解码器。
只有满足entry.handles(dataClass, resourceClass),才能被添加返回:
public boolean handles(@NonNull Class<?> dataClass, @NonNull Class<?>resourceClass) {return this.dataClass.isAssignableFrom(dataClass) &&resourceClass
.isAssignableFrom(this.resourceClass);
}
由于我们的resourceClass是Object,因此resourceClass .isAssignableFrom(this.resourceClass)总是成立的,所以就看:this.dataClass.isAssignableFrom(dataClass)
而我们的dataClass是InputStream,打开各种类型,可以看到哪些的dataClass是InputStream:
上面框错了,应该框resourceClass,另外FrameSequenceDrawable是我自定义后注册进去的,所以Glide原生的是没有的。
所以最终返回的resource为:
接下来是针对每一种resourceClass获取对应的转码类(要转成的对象):
public synchronized <Z, R> List<Class<R>>getTranscodeClasses(
@NonNull Class<Z> resourceClass, @NonNull Class<R>transcodeClass) {
List<Class<R>> transcodeClasses = new ArrayList<>();//GifDrawable -> Drawable is just the UnitTranscoder, as is GifDrawable -> GifDrawable. if(transcodeClass.isAssignableFrom(resourceClass)) {
transcodeClasses.add(transcodeClass);returntranscodeClasses;
}for (Entry<?, ?>entry : transcoders) {if(entry.handles(resourceClass, transcodeClass)) {
transcodeClasses.add(transcodeClass);
}
}returntranscodeClasses;
}
如果transcodeClass是resourceClass的父类那就直接返回。
第一个GifDrawable,返回的registeredTranscodeClasses为:
然后根据dataClass, registeredResourceClass获取decoders:
然后根据registeredResourceClass和registeredTranscodeClass获取transcoder
上面具体的获取过程是类似的,就不过多分析了。
然后构造DecodePath,放进下面的集合里面:
List<DecodePath<Data, TResource, Transcode>> decodePaths = new ArrayList<>();
循环获取之后,最终得到的decodePaths如下:
大致流程:
1、先根据传进来的resourceClass获取注册表中所有注册的resourceClass得到List<Class<TResource>> registeredResourceClasses
2、两层for循环:
(1)外层:根据registeredResourceClasses获取转码的class :List<Class<Transcode>> registeredTranscodeClasses
(2)内层:
a、根据资源resourceClass获取所有的解码器。
b、根据资源resourceClass和转码transcodeClass获取所有的转码器。
c、构造DecodePath,放进集合里面。
最后得到的List<DecodePath<Data, TResource, Transcode>> decodePaths被放到LoadPath对象里面(上一层方法可看到)
我们又回到DecodeJob中的方法:
private <Data> Resource<R>decodeFromFetcher(Data data, DataSource dataSource)throwsGlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());returnrunLoadPath(data, dataSource, path);
}
获取到LoadPath后接下来就是要开始执行了runLoadPath了。
找下去可以看到该方法:
returnpath.load(
rewinder, options, width, height,new DecodeCallback<ResourceType>(dataSource));
该方法属于LoadPath对象。
层层追溯后,最终来到下面的方法:
private Resource<Transcode> loadWithExceptionList(DataRewinder<Data>rewinder,
@NonNull Options options,int width, int height, DecodePath.DecodeCallback<ResourceType>decodeCallback,
List<Throwable> exceptions) throwsGlideException {
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));
}returnresult;
}
该方法在LoadPath里面,遍历decodePaths(这是我们之前获取后放在LoadPath中的)进行解码:
result = path.decode(rewinder, width, height, options, decodeCallback);
然后来到:
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, intheight,
@NonNull Options options, DecodeCallback<ResourceType> callback) throwsGlideException {
Resource<ResourceType> decoded =decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed =callback.onResourceDecoded(decoded);returntranscoder.transcode(transformed, options);
}
我们这里需要看的就是:decodeResource:
最终来到DecodePath里面的方法:
@NonNullprivate Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, intwidth,int height, @NonNull Options options, List<Throwable> exceptions) throwsGlideException {
Resource<ResourceType> result = null;//noinspection ForLoopReplaceableByForEach to improve perf for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder<DataType, ResourceType> decoder =decoders.get(i);try{
DataType data=rewinder.rewindAndGet();if(decoder.handles(data, options)) {
data=rewinder.rewindAndGet();
result=decoder.decode(data, width, height, options);
}//Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but//instead log and continue. See #2406 for an example. } catch (IOException | RuntimeException |OutOfMemoryError e) {if(Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG,"Failed to decode data for " +decoder, e);
}
exceptions.add(e);
}if (result != null) {break;
}
}if (result == null) {throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}returnresult;
}
这个方法:decoder.handles(data, options)是判断该解码器是否可以对该资源进行解码,这个方法写在每个解码器里面。
DataRewinder里面放着需要进行解码的数据。
解码后将资源返回。
又回到这个方法:
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, intheight,
@NonNull Options options, DecodeCallback<ResourceType> callback) throwsGlideException {
Resource<ResourceType> decoded =decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed =callback.onResourceDecoded(decoded);returntranscoder.transcode(transformed, options);
}
这一句Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
是对资源进行变换处理,比如图片的缩放,剪裁等等,这个功能单独拎出来讲。
接下来便是运用转码器进行资源的转码:
transcoder.transcode(transformed, options)
到此就结束了。