本文基于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源码解析四(解码和转码)的更多相关文章

  1. Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?

    Mybatis源码解析(四) -- SqlSession是如何实现数据库操作的?   如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容.正如标题一 ...

  2. Sentinel源码解析四(流控策略和流控效果)

    引言 在分析Sentinel的上一篇文章中,我们知道了它是基于滑动窗口做的流量统计,那么在当我们能够根据流量统计算法拿到流量的实时数据后,下一步要做的事情自然就是基于这些数据做流控.在介绍Sentin ...

  3. Dubbo 源码解析四 —— 负载均衡LoadBalance

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...

  4. iOS即时通讯之CocoaAsyncSocket源码解析四

    原文 前言: 本文为CocoaAsyncSocket源码系列中第二篇:Read篇,将重点涉及该框架是如何利用缓冲区对数据进行读取.以及各种情况下的数据包处理,其中还包括普通的.和基于TLS的不同读取操 ...

  5. React的React.createContext()源码解析(四)

    一.产生context原因 从父组件直接传值到孙子组件,而不必一层一层的通过props进行传值,相比较以前的那种传值更加的方便.简介. 二.context的两种实现方式 1.老版本(React16.x ...

  6. Spring IOC容器启动流程源码解析(四)——初始化单实例bean阶段

    目录 1. 引言 2. 初始化bean的入口 3 尝试从当前容器及其父容器的缓存中获取bean 3.1 获取真正的beanName 3.2 尝试从当前容器的缓存中获取bean 3.3 从父容器中查找b ...

  7. AFNetworking2.0源码解析<四>

    结构 AFURLResponseSerialization负责解析网络返回数据,检查数据是否合法,把NSData数据转成相应的对象,内置的转换器有json,xml,plist,image,用户可以很方 ...

  8. Celery 源码解析四: 定时任务的实现

    在系列中的第二篇我们已经看过了 Celery 中的执行引擎是如何执行任务的,并且在第三篇中也介绍了任务的对象,但是,目前我们看到的都是被动的任务执行,也就是说目前执行的任务都是第三方调用发送过来的.可 ...

  9. MyBatis 3源码解析(四)

    四.MyBatis 查询实现 Employee empById = mapper.getEmpById(1); 首先会调用MapperProxy的invoke方法 @Override public O ...

  10. vuex 源码解析(四) mutation 详解

    mutation是更改Vuex的store中的状态的唯一方法,mutation类似于事件注册,每个mutation都可以带两个参数,如下: state ;当前命名空间对应的state payload ...

随机推荐

  1. 文盘Rust -- r2d2 实现redis连接池

    作者:贾世闻 我们在开发应用后端系统的时候经常要和各种数据库.缓存等资源打交道.这一期,我们聊聊如何访问redis 并将资源池化. 在一个应用后端程序访问redis主要要做的工作有两个,单例和池化. ...

  2. vue3封装搜索表单组件

    seacrch 表单完成的功能 1.根据配置json配置项自动生成表单 ok 2.是响应式的排版 ok 3.点击搜索按钮会向上抛出值 ok 4.点击重置按钮会自动清空数据,不需要父组件额外的处理 ok ...

  3. elementui表格内容超出显示省略号

    有些时候表格的内容太长了: 但是elementui中的表格,会进行换行处理: 此时表格的高度就会发生变化 这样就不好看,此时就要进行省略号来出来这个问题: el-table是有这个控制属性的::sho ...

  4. 【二】gym初次入门一学就会---代码详细解析简明教程----平衡杆案例

    相关文章: [一]gym环境安装以及安装遇到的错误解决 [二]gym初次入门一学就会-简明教程 [三]gym简单画图 [四]gym搭建自己的环境,全网最详细版本,3分钟你就学会了! [五]gym搭建自 ...

  5. 1.5 为x64dbg编写插件

    任何一个成熟的软件都会具有可扩展性,可扩展性是现代软件的一个重要特征,因为它使软件更易于维护和适应变化的需求,x64dbg也不例外其可通过开发插件的方式扩展其自身功能,x64dbg提供了多种插件接口, ...

  6. C/C++ x32 Inline Hook 代码封装

    Hook 技术常被叫做挂钩技术,挂钩技术其实早在DOS时代就已经存在了,该技术是Windows系统用于替代DOS中断机制的具体实现,钩子的含义就是在程序还没有调用系统函数之前,钩子捕获调用消息并获得控 ...

  7. MySQL 数据库读写分离

    MySQL 是最流行的关系型数据库管理系统,MySQL 配置主备模式,基于一台服务器的数据复制,故得名单机热备,主-备 Active-Standby主-备方式,即指的是一台服务器处于某种业务的激活状态 ...

  8. 离线解锁 CodeCombat 全关卡教程 使用docker安装实现

    前期准备 下载安装docker desktop https://www.123pan.com/s/fmvUVv-HqApH, 这个安装不会的随便搜一个教程,挺多的.我随便找了一个知乎的 Windows ...

  9. JS leetcode 两数之和 II - 输入有序数组 题解分析

    壹 ❀ 引 我在JS leetcode 两数之和 解答思路分析一文中首次解决两数之和等于目标值的问题,那么今天遇到的是两数之和的升级版,题目为leetcode167. 两数之和 II - 输入有序数组 ...

  10. NC13950 Alliances

    题目链接 题目 题目描述 树国是一个有n个城市的国家,城市编号为1∼n.连接这些城市的道路网络形如一棵树, 即任意两个城市之间有恰好一条路径.城市中有k个帮派,编号为1∼k.每个帮派会占据一些城市,以 ...