Glide源码解析四(解码和转码)
本文基于Glide 4.11.0
Glide加载过程有一个解码过程,比如将url加载为inputStream后,要将inputStream解码为Bitmap。
从Glide源码解析一我们大致知道了Glide加载的过程,所以我们可以直接从这里看起,在这个过程中我们以从文件中加载bitmap为例:
DecodeJob的一个方法:
private void decodeFromRetrievedData() {
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)
throws GlideException {
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) {
return glideContext.getRegistry().getLoadPath(dataClass, resourceClass, transcodeClass);
}
---------------->
@Nullable
public <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);
}
return result;
}
首先会先从缓存中拿,缓存中拿不到再通过下面的方法去拿:
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);
}
}
return decodePaths;
}
该方法各个参数如下:
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);
}
}
}
return result;
}
该方法是为了获取解码器中的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);
return transcodeClasses;
}
for (Entry<?, ?> entry : transcoders) {
if (entry.handles(resourceClass, transcodeClass)) {
transcodeClasses.add(transcodeClass);
}
}
return transcodeClasses;
}
如果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)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
return runLoadPath(data, dataSource, path);
}
获取到LoadPath后接下来就是要开始执行了runLoadPath了。
找下去可以看到该方法:
return path.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) 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;
}
该方法在LoadPath里面,遍历decodePaths(这是我们之前获取后放在LoadPath中的)进行解码:
result = path.decode(rewinder, width, height, options, decodeCallback);
然后来到:
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
我们这里需要看的就是:decodeResource:
最终来到DecodePath里面的方法:
@NonNull
private Resource<ResourceType> decodeResourceWithList(DataRewinder<DataType> rewinder, int width,
int height, @NonNull Options options, List<Throwable> exceptions) throws GlideException {
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));
}
return result;
}
这个方法:decoder.handles(data, options)是判断该解码器是否可以对该资源进行解码,这个方法写在每个解码器里面。
DataRewinder里面放着需要进行解码的数据。
解码后将资源返回。
又回到这个方法:
public Resource<Transcode> decode(DataRewinder<DataType> rewinder, int width, int height,
@NonNull Options options, DecodeCallback<ResourceType> callback) throws GlideException {
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
这一句Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
是对资源进行变换处理,比如图片的缩放,剪裁等等,这个功能单独拎出来讲。
接下来便是运用转码器进行资源的转码:
transcoder.transcode(transformed, options)
到此就结束了。
转载请标明:https://www.cnblogs.com/tangZH/p/12912698.html
Glide源码解析四(解码和转码)的更多相关文章
- Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?
Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的? 如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...
- Sentinel源码解析四(流控策略和流控效果)
引言 在分析Sentinel的上一篇文章中,我们知道了它是基于滑动窗口做的流量统计,那么在当我们能够根据流量统计算法拿到流量的实时数据后,下一步要做的事情自然就是基于这些数据做流控.在介绍Sentin ...
- Dubbo 源码解析四 —— 负载均衡LoadBalance
欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...
- iOS即时通讯之CocoaAsyncSocket源码解析四
原文 前言: 本文为CocoaAsyncSocket源码系列中第二篇:Read篇,将重点涉及该框架是如何利用缓冲区对数据进行读取.以及各种情况下的数据包处理,其中还包括普通的.和基于TLS的不同读取操 ...
- React的React.createContext()源码解析(四)
一.产生context原因 从父组件直接传值到孙子组件,而不必一层一层的通过props进行传值,相比较以前的那种传值更加的方便.简介. 二.context的两种实现方式 1.老版本(React16.x ...
- Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段
目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...
- AFNetworking2.0源码解析<四>
结构 AFURLResponseSerialization负责解析网络返回数据,检查数据是否合法,把NSData数据转成相应的对象,内置的转换器有json,xml,plist,image,用户可以很方 ...
- Celery 源码解析四: 定时任务的实现
在系列中的第二篇我们已经看过了 Celery 中的执行引擎是如何执行任务的,并且在第三篇中也介绍了任务的对象,但是,目前我们看到的都是被动的任务执行,也就是说目前执行的任务都是第三方调用发送过来的.可 ...
- MyBatis 3源码解析(四)
四.MyBatis 查询实现 Employee empById = mapper.getEmpById(1); 首先会调用MapperProxy的invoke方法 @Override public O ...
- vuex 源码解析(四) mutation 详解
mutation是更改Vuex的store中的状态的唯一方法,mutation类似于事件注册,每个mutation都可以带两个参数,如下: state ;当前命名空间对应的state payload ...
随机推荐
- hadoop实践02---eclipse操作hdfs的api上传文件
1.eclipse中编写代码后双击main方法--->Run as ---> java application ,然后指定的文件 就会提交到hdfs中. 2.查看文件:http://192 ...
- TienChin 渠道管理-渠道导入
ChannelController @PostMapping("/importTemplate") void importTemplate(HttpServletResponse ...
- 突破性的多语言代码大模型基CodeShell:引领AI编程新时代
突破性的多语言代码大模型基CodeShell:北京大学与四川天府银行联合打造,引领AI编程新时代 1.CodeShell简介 CodeShell是北京大学知识计算实验室联合四川天府银行AI团队研发的多 ...
- 【3】VSCode 主题设置推荐,自定义配色方案,修改注释高亮颜色
相关文章: [一]tensorflow安装.常用python镜像源.tensorflow 深度学习强化学习教学 [二]tensorflow调试报错.tensorflow 深度学习强化学习教学 [三]t ...
- Celery Worker log 中记录 task_id
import inspect import logging import threading from logging import Logger as Logger, LogRecord from ...
- tp使用workerman消息推送
安装 首先通过 composer 安装 composer require topthink/think-worker SocketServer 在命令行启动服务端 php think worker:s ...
- 20.4 延迟加载DLL--《Windows核心编程》
延迟加载的 DLL 是个隐含链接的 DLL,它实际上要等到你的代码试图引用 DLL 中包含的一个符号时才进行加载. DLL延迟加载技术的原理,就是从导入表中去掉某dll这一项,等到正式调用DLL的时候 ...
- delphi中 注意一点,record 类型 参数默认是 值拷贝,class 参数 默认是传地址;值传递,指针传递、引用传递
作为函数的入参,若是record类型,默认是值拷贝,效率低,若要传指针,需要加 var ; 作为函数的入参,若是 class类型,默认是传地址,不需要加var unit Unit1; interfac ...
- Centos7部署django+uwsgi+nginx 转载: https://www.cnblogs.com/wztshine/p/16172154.html
Centos7部署django+uwsgi+nginx Django + uwsgi + nginx on Centos7 wsgi 介绍 本小节来自 廖雪峰 wsgi 一文 一个Web应用的本质 ...
- NC52867 Highway
题目链接 题目 题目描述 In ICPCCamp there were n towns conveniently numbered with \(1, 2, \dots, n\) connected ...