展开说说Android之Glide详解_源码解析

基于上一篇介绍了Glide的使用篇本文分析一下Glide的源码实现,看看我们简单几步就实现的图片展示功能在源码中是怎样完成的。

一、Glide中的核心文件

先逐个介绍一下个人以为的几个核心类:‌

1、Glide

Glide是必经的入口,通过Glide.get(context)主要是做初始化,包括、Registry、RequestManagerRetriever等核心组件。

2、RequestManagerRetriever

获取RequestManager,创建无UI的RequestManagerFragment绑定生命周期监听,通过Lifecycle监听宿主生命周期。

3、GlideBuilder 

GlideBuilder 它的build方法会配置线程池、缓存策略等初始化Engine以及初始化Glide对象。

4、RequestBuilder

构建请求参数,asDrawableasGifasBitmap加载不同类型,默认asDrawable最终由Engine调度三级缓存(活动资源/LRU内存/磁盘)

5、RequestManager

管理请求队列,协调生命周期。

6、RequestManagerFragment

‌绑定生命周期‌非Application上下文:创建无UI的RequestManagerFragment,通过Lifecycle监听宿主生命周期。主线程调用时通过FragmentManager管理Fragment,子线程使用Application级生命周期。

7、LifecycleListener

定义生命周期的接口,里面onStartonStoponDestroy三个方法与宿主Fragment或Activity的同名生命周期绑定。

8、Engine

合理调度三级缓存(调取活动资源/内存/磁盘)

9、Target

Target是一个接口,继承自LifecycleListener并且额外增加了一些加载过程中的方法,比如onLoadStartedonLoadFailedonLoadCleared等等。他还有一个抽象的实现类BaseTarget

后面子子孙孙还有很多抽象类继承BaseTarget,比如下一代ViewTarget、下下代ImageViewTarget

、下下下代是BitmapImageViewTargetDrawableImageViewTarget以及ThumbnailImageViewTarget,他们才是into方法传入的实际展示图片的控件。

是借助ImageViewTargetFactory来创建对应Target(如BitmapImageViewTarget)。

10、DecodeJob 

看得出来他是做解码工作的,缓存未命中时创建DecodeJob,提交到EngineJob线程池。实现了Runnable接口因此再run方法中借助DataFetcher进行解码。

11、DataFetcher

上面提到了借助DataFetcher进行解码,但他是个接口,所以真正工作是它的实现类们,比如FileFetcherAssetPathFetcherHttpUrlFetcher等都不同地址来源的图片进行解码。

12、Registry

组件注册中心,支持扩展ModelLoader等模块根据模型类型(如String/File)匹配对应的ModelLoader

二、分阶段梳理工作流程

1、注

Glide.with(mActivity).load(R.drawable.czzs_step_station).into(image);

以在Activity中加载为例,这也是使用最多的场景。其他场景请按此思路自行梳理。

2、初始化阶段‌:

Glide.with()通过RequestManagerRetriever获取RequestManager,绑定生命周期监听

2.1 获取RequestManager

public static RequestManager with(Activity activity) {return getRetriever(activity).get(activity);
}

2.2 获取RequestManagerRetriever

private static RequestManagerRetriever getRetriever(@Nullable Context context) {// Context could be null for other reasons (ie the user passes in null), but in practice it will// only occur due to errors with the Fragment lifecycle.Preconditions.checkNotNull(context,"You cannot start a load on a not yet attached View or a  Fragment where getActivity() "+ "returns null (which usually occurs when getActivity() is called before the Fragment "+ "is attached or after the Fragment is destroyed).");return Glide.get(context).getRequestManagerRetriever();
}

顺着捋会找到Glide中的initializeGlide方法,此处代码角度,只贴出实例化GlideBuilder以及通过它的build方法创建Glide对象,并将Glide对象声明为成员变量。

GlideBuilder builder = new GlideBuilder().setRequestManagerFactory(factory);
for (GlideModule module : manifestModules) {module.applyOptions(applicationContext, builder);
}
if (annotationGeneratedModule != null) {annotationGeneratedModule.applyOptions(applicationContext, builder);
}
Glide glide = builder.build(applicationContext);
for (GlideModule module : manifestModules) {module.registerComponents(applicationContext, glide, glide.registry);
}
if (annotationGeneratedModule != null) {annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
Glide.glide = glide;

然后上面的builder.build(applicationContext);中初始化了EngineRequestManagerRetriever

以及Glid,当然也创建了线程池,只关键部分代码:

 if (engine == null) {engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor,GlideExecutor.newUnlimitedSourceExecutor());}RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory);return new Glide(context,engine,memoryCache,bitmapPool,arrayPool,requestManagerRetriever,connectivityMonitorFactory,logLevel,defaultRequestOptions.lock());
}

2.3 绑定生命周期

现在看2.1中后半部分get(activity)这里是调用RequestManagerRetriever的get方法:

public RequestManager get(Activity activity) {if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());} else {assertNotDestroyed(activity);android.app.FragmentManager fm = activity.getFragmentManager();return fragmentGet(activity, fm, null /*parentHint*/);}
}

这里出现了FragmentManager它就是要创建无UI的RequestManagerFragment,通过Lifecycle监听宿主生命周期 创建fragment的代码:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm, android.app.Fragment parentHint) {RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if (current == null) {current = pendingRequestManagerFragments.get(fm);if (current == null) {current = new RequestManagerFragment();current.setParentFragmentHint(parentHint);pendingRequestManagerFragments.put(fm, current);fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();}}return current;
}

现在准备工作基本完成了。

2、‌加载阶段‌:

load(R.drawable.czzs_step_station).into(image);

传入图片路径和ImageView组件。

public RequestBuilder<Drawable> load(@Nullable Object model) {return asDrawable().load(model);
}
RequestBuilder构建请求参数,最终由Engine调度三级缓存(活动资源/LRU内存/磁盘)。
public Target<TranscodeType> into(ImageView view) {省略很多行。。。return into(context.buildImageViewTarget(view, transcodeClass));
}

buildImageViewTarget方法创建一个Target实例,其实就是上面提到的BitmapImageViewTargetDrawableImageViewTarget

@SuppressWarnings("unchecked")
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {if (Bitmap.class.equals(clazz)) {return (Target<Z>) new BitmapImageViewTarget(view);} else if (Drawable.class.isAssignableFrom(clazz)) {return (Target<Z>) new DrawableImageViewTarget(view);} else {throw new IllegalArgumentException("Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");}
}

然后进入RequestManager的track方法:

void track(Target<?> target, Request request) {targetTracker.track(target);requestTracker.runRequest(request);
}

下一步

public void runRequest(Request request) {requests.add(request);if (!isPaused) {request.begin();} else {pendingRequests.add(request);}
}

进入Request的实现类SingleRequest中的begin方法开始处理加载。

3、解码阶段‌:

解码是用DataFetcher和它的子实现类们:

这是加载结果的两个回调:

这里说一下Glide是怎样加载网络图片的,没错,和其他网络请求一样她也是用的HttpURLConnection

在它的实现类HttpUrlFetcherloadData方法:

@Override
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {long startTime = LogTime.getLogTime();final InputStream result;try {result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,glideUrl.getHeaders());} catch (IOException e) {if (Log.isLoggable(TAG, Log.DEBUG)) {Log.d(TAG, "Failed to load data for url", e);}callback.onLoadFailed(e);return;}if (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)+ " ms and loaded " + result);}callback.onDataReady(result);
}

这里会将结果callback.onDataReady(result);毁掉了上面截图中提到的onDataReady方法。

调用loadDataWithRedirects方法里:

这里如果失败还会重新请求连接,最多重连五次:

private static final int MAXIMUM_REDIRECTS = 5;

最后各种通过FetcherReadyCallback以及其他各种回调,最终在BitmapImageViewTarget展示图片内容:

/*** Sets the {@link android.graphics.Bitmap} on the view using {@link* android.widget.ImageView#setImageBitmap(android.graphics.Bitmap)}.** @param resource The bitmap to display.*/
@Override
protected void setResource(Bitmap resource) {view.setImageBitmap(resource);
}

三、其实还有一些其他很重要的公共类:

有一个线程池:

还一个上下文:

一个异常类:

Glide是一个非常优秀的框架,源码架构设计的太好了。另外想了解Glide使用的朋友可以参考上一篇《展开说说Android之Glide详解_使用篇》。

个人总结记录,才疏学浅,如有错误,欢迎指正,多谢。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/87185.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

商品中心—6.商品考核系统的技术文档二

大纲 1.基于大数据系统的商品考核数据指标 2.基于商品考核数据指标的商品考核流程 3.商品考核失败后的处理 考核流程的设计 4.商品考核系统数据库模型设计 5.商品考核系统核心接口 6.商品生命周期系统的定时考核任务 6.商品生命周期系统的定时考核任务 (1)定时任务处理…

鸿蒙组件通用事件开发全攻略:从基础交互到工程实践

一、引言&#xff1a;事件系统 —— 构建交互体验的核心枢纽 在鸿蒙应用开发体系中&#xff0c;组件事件系统是连接用户操作与应用逻辑的关键桥梁。从基础的点击交互到复杂的多触点手势&#xff0c;通用事件覆盖了全场景设备的交互需求。本文将系统解构鸿蒙事件体系的核心机制…

老项目重构难题破解:飞算 JavaAI 如何实现技术升级突围

在企业数字化转型进程中&#xff0c;大量 Java 老项目因长期迭代积累的技术债务&#xff0c;陷入 "重构必要性与实施难度并存" 的困境。这些遗留系统普遍存在代码体系老化、架构模式滞后、维护成本高企等问题&#xff0c;成为企业技术升级的绊脚石。 传统 Java 老项…

idea使用技巧分享

写在前面 分享一些常用的idea使用技巧&#xff0c;进来看看有没有你不知道的。 设置项目默认配置 TODO设置 位置 方式一&#xff1a;setting -> editor -> TODO 方式二&#xff1a; 定义Patterns过滤模式 正则中“\b”是元字符代表着单词的开头或结尾&#xff0c;也就…

【Dify精讲】第8章:Agent能力实现原理【知识卡片】

第8章&#xff1a;Agent能力实现原理http://www.airinto.com/share/e7b7e27f 一、Agent架构设计 二、工具调用机制 三、ReAct框架实现 四、自定义Agent开发 五、性能优化与监控 六、总结与实战建议

【软件】安装Miniconda

安装 根据搜索结果&#xff0c;以下是使用Homebrew在macOS上安装Miniconda的详细步骤&#xff1a; 1.安装Homebrew&#xff08;如果尚未安装&#xff09; 打开终端&#xff08;Terminal&#xff09;&#xff0c;运行以下命令安装Homebrew&#xff1a; /bin/bash -c "$(…

FastAPI:(6)错误处理

FastAPI&#xff1a;(6)错误处理 由于CSDN无法展示「渐构」的「#d&#xff0c;#e&#xff0c;#t&#xff0c;#c&#xff0c;#v&#xff0c;#a」标签&#xff0c;推荐访问我个人网站进行阅读&#xff1a;Hkini 「渐构展示」如下&#xff1a; #c 概述 文章概念关系 graph TDA…

408第一季 - 数据结构 - 排序

排序的概念 外部排序很难&#xff0c;后面都是内部排序 插入排序 直接插入排序 理解 这个排序第一轮是从第二个元素开始的 然后是从后往前一个一个比的 然后我们看i5的情况&#xff0c;会出现比较次数和移动次数的概念&#xff0c;这里97动了 然后i8时&#xff0c;49最好…

高效账号信息管理工具,可安全随机生成密码

软件介绍 今天给大家推荐一款安全可靠的密码管理工具&#xff0c;帮助用户轻松管理各类账号密码。 安全便捷的密码解决方案 这是一款采用先进加密技术开发的密码管理器&#xff0c;不仅可以生成高强度随机密码&#xff0c;还提供安全的账号密码备份存储功能。 基础安全设置 …

如何在markdown文件中(博客)添加emoji表情,让你的博客看起来更加优雅

在Markdown中使用Emoji的完整指南 按分类快速参考的完整Emoji列表一、状态指示类:bulb:二、提示信息类:bulb:三、内容类型类:bulb:四、操作指令类:bulb:五、进度状态类:bulb:六、技术相关类:bulb:七、人员角色类:bulb:八、版本控制类:bulb: 你学会了吗 按分类快速参考的完整Emo…

MAZANOKE:一款隐私优先的浏览器图像优化工具及Docker部署指南

在日常工作中&#xff0c;大家是否经常遇到这样的需求&#xff1a;需要压缩图片体积、调整图片尺寸或转换图片格式&#xff0c;但又受限于数据安全要求无法将图片上传至公网&#xff1f;在我们之前开发的工单配置系统中&#xff0c;这类需求尤为常见。最近在GitHub上发现了一款…

【Vue PDF】Vue PDF 组件初始不加载 pdfUrl 问题分析与修复

Vue PDF 组件初始不加载 pdfUrl 问题分析与修复 问题现象 在开发 PDF 预览组件时&#xff0c;遇到这样一个问题&#xff1a; 初始状态下&#xff0c;PDF 组件不会请求 pdfUrl&#xff08;即不会加载 PDF 文件&#xff09;。只有点击"全屏"按钮后&#xff0c;才会请…

《注解的江湖:一场元数据的“宫斗剧”》

一、你真的懂注解吗 你是否使用过Autowired却不知道是如何生效的&#xff1f; 这几个注解你一定很熟悉&#xff1a; OverrideDeprecatedTransactional 那么你有进一步思考过怎么生效的吗&#xff1f;注解到底是什么&#xff1f;注解&#xff0c;到底是信息&#xff1f;还是指…

智能土木通 - 土木工程专业知识问答系统02-RAG检索模块搭建

一、项目目录 civil_qa_system/ ├── docs/ # 项目文档 ├── config/ # 配置文件 ├── core/ # 核心功能代码 ├── knowledge_base/ # 知识库相关 ├── web/ # Web应用部分 ├…

进程和线程区别、管道和套接字、共享变量、TCP三次握手,是否可以少一次握手、子进程和主进程区别和API——Nodejs

首先讲了进程和线程区别 然后讲解 管道和套接字&#xff0c;它是进程间通信的方式 接着讲解共享变量 &#xff0c;它是线程间通信 最后讲解TCP三次握手&#xff0c;因为套接字使用了TCP协议 一、线程和进程的区别 线程&#xff08;Thread&#xff09;和进程&#xff08;Pr…

docker(学习笔记第一课) 使用nginx +https + wordpress

文章目录 docker(学习笔记第一课) 使用nginx https wordpress学习内容&#xff1a;1. 整体架构1.1 在aws ec2的整体架构1.2 不懂都可以问AI 2. 构建详细2.1 构建ec22.2 安装docker2.3 创建一个docker的内部network2.4 创建wordpress使用的mysql数据库2.5 创建两个wordpress的d…

Leetcode 刷题记录 15 —— 二分查找

本系列为笔者的 Leetcode 刷题记录&#xff0c;顺序为 Hot 100 题官方顺序&#xff0c;根据标签命名&#xff0c;记录笔者总结的做题思路&#xff0c;附部分代码解释和疑问解答&#xff0c;01~07为C语言&#xff0c;08及以后为Java语言。 01 搜索插入位置 class Solution {pub…

C++核心编程(动态类型转换,STL,Lanmda)

一. 类型转换 二. STL 1. 容器 1.1 Vector&#xff08;常用&#xff09; 1.1.1 概述 特性&#xff1a; 动态数组&#xff1a; 想象成一个会自动变长变短的数组。起始在内存中是连续存储的。 随机访问&#xff1a; 通过[]运算符或at()方法&#xff0c;可以瞬间&#xff08;…

【图像处理入门】8. 数学基础与优化:线性代数、概率与算法调优实战

摘要 图像处理的核心离不开数学工具的支撑。本文将深入解析线性代数、概率论在图像领域的应用,包括矩阵变换与图像几何操作的关系、噪声模型的数学描述,以及遗传算法、粒子群优化等智能算法在参数调优中的实践。通过理论结合代码案例,帮助读者掌握从数学原理到工程优化的完…

操作系统八股文

一.进程和线程的区别 1.本质区别和所属关系是什么&#xff1f; 进程是资源调度以及分配的基本单位。 线程是CPU调度的基本单位。 一个线程属于一个进程&#xff0c;一个进程可以拥有多个线程。 2.地址空间和内存 进程拥有独立的虚拟地址空间。 线程没有独立的地址空间&#xf…