Glide完全解读

一,概述

glide作为android流行的图片加载框架,笔者认为有必要对此完全解读。glide提供了三级缓存、生命周期Destroy后自动移除缓存、自动适配ImageView,以及提供了各种对图片修饰的操作,如剪裁等。本文通过最简单的使用,挖掘出with、load、into三大核心函数的源码逻辑,进一步理解Glide设计思路。读者可顺着笔者粗糙的源码流程更深入地解读。

二,简单使用

如上,笔者不赘述。

三,核心流程

1,with

众所周知,Glide支持监听生命周期,那么它是怎么做的呢?我们看下with函数的重载,返回值是RequestManager。

笔者查阅官方注释,推测RequestManager负责调度一次请求,如停止、启动或重启,这需要与生命周期者配合。因此,笔者进一步推测RequestManager与生命周期者,即android中的Activity、Fragment、Application是1:1的关系。

从以上RequestManager提供的public方法来看,提供了诸如加载load,pause,resume,clear等调度函数。onStart和onStop是其回调函数,如下

在onStart时resumeRequest,onStop时清除或pauseRequest,取决于clearOnStop字段的设置。

笔者继续更加with函数,看下RequestManager如何创建。

笔者以with重载函数参数为context为例,

首先调用getRetriever获得RequestManagerRetriever,这是负责创建和复用RequestManager的工厂类,全局单例。

RequestManagerRetirever在Glide的构造函数中被初始化,因Glide是单例模式,因此RequestManagerRetirever也是单例。我们跟进到RequestManagerRetirever#get方法,也有很多重载,保持glide#with重载一样。

笔者仍以context为例,

(1)如果context是FragmentActivity,且,当前调用with函数来自主线程,我们跟进

来到重载get(FragmentActivity),如果当前是子线程则进入重载get(Application)。同上述流程,我们跟进getOrCreate,注意传入了FragmentActivity的FragmentManager。在此之前,我们看下LifecycleRequestManagerRetriever的成员。

根据lifecycleToRequestManager这个Map,笔者验证了RequestManager与生命周期者是1:1关系。

接着看getOrCreate

如果不存在RequestManager,则new一个LifeCycleLifeCycle,lifecycle传入,

LifeCycleLifeCycle实现了LifecycleObserver接口,这样就能监听到lifecycle的生命周期了。通过工厂方法build创建,其实现在RequestManagerRetriever,简单new了一个RequestManager,

我们紧接后续逻辑,通过isParentVisible判断是否调用result.onStart回调,result就是RequestManager了,onStart即恢复请求,笔者这是第一次从with进入,因此没有请求。

唉,看到这,笔者发现glide优化了,毕竟在旧版本,glide是通过在FragmentActivity中创建一个隐藏的空Fragment去监听Activity的生命周期,当时笔者就有疑问,为什么不直接监听呢?

笔者注意到,lifeCycle走到onDestroy时自动移除对应的RequestManager。

笔者也注意到,SupportRequestManagerTreeNode这个类,看下实现,

通过getDescendants可知,获得当前FragmentManager的所有RequestManager,包括子片段。主要在调度Request Resume或Pause使用,其意义是递归式地暂停或恢复当前LifeCycle及其附着的子LifeCycle所对应的RequestManager。

笔者继续看其他get方法的重载实现,

再看下Application的get重载,

可知applicationManager全局单例,并且由于传入了ApplicationLifecycle,自然调用不到RequestManager的onStart和onStop方法,没有自动暂停恢复功能。

综上,笔者从多个get方法得出如下结论:

(1),如果传入Applicaton,全局单例,无生命周期监听功能。

(2),如果传入Activity,则直接调用到(1)。

(3),如果传入FragmentActivity,如果在子线程调用到(1),否则创建或复用该FragmentActivity对应的RequestManager,能监听生命周期。

(4),如果传入Fragment,将当前Fragment作为生命周期对象监听,并且创建或复用一个RequestManager。

(5),如果传入View,通过其getContext决定调用到(1)或(3)或(4)。

即with方法决定了在当前上下文中获取的复用RequestManager。

2,load

在load之前,笔者首先看下其as方法,这是RequestManager创建一次RequestBuilder的起点,需读者注意,RequestBuilder继承了BaseRequestOptions类,

可以接收的参数有

(1)BItmap.class,

(2)Drawable.class,

(3)File.class,

(4)GitDrawable.class,

保存至transcodeClass中,并且在构造中根据其类型,获得一些默认的options,这个读者可自行了解。

笔者看下load方法的多个重载,假设以Drawable重载为例跟进

最终都会调用到loadGeneric,并且将load传入的参数保存到model中,以构造者设计方式设计此RequestBuilder,

RequestBuilder其重要是apply函数,

负责保存对图片的操作,每个操作对应RequestOptions类,方法如下

看到这,load方法就结束了。做了四件事情,

(1)创建一个RequestBuilder对象,

(2)保存图片目标类,记录在RequestBuilder#transcodeClass。

(3)保存图片来源,记录在RequestBuilder#mode。

(4)保存对图片的各种RequestOptions操作,记录在RequestBuilder的Set集合中。

对于缓存的实现,主要在RequestOptionsr和RequestBuilder的父类BaseRequestOptions中,通过一系列成员保存option操作,读者可自行研究。对于缓存策略,笔者将在into中讲述。

3,into

target必须是实现了Target接口,看下Target定义如下,

笔者在这里理解为资源相关的回调,如资源加载开始、失败、完成、清除等,其接收一个泛型字段,TranscodeType,这个和RequestBuilder中的transcodeClass相同。

再看下Target的实现类,

笔者在此发现了ImageViewTarget这个类,因此推测into(ImageView)是将ImageView封装进ImageVIewTarget对象,其根据transcodeClass决定使用BitmapImageVIewTarget还是DrawableImageVIewTarget。另外,Target接口继承了LifecycleListener接口,笔者推测此处与RequestManager联合,做一些事情,如动画相关等等等。

笔者跟进Into,最终全部调用到private的into重载,

笔者注意到传入callbackExecutor是Executor.mainThreadExecutor。

通过buildRequest方法创建一个Request,Request是一个接口,看下定义,

很简单,负责一次请求的开始、清除、暂停,以及状态获取。只有如下三个实现,

那么,具体返回哪个Request呢,笔者继续跟进buildRequestRecursive。

现创建mainRequest,其是正常请求,如果我们设置了加载失败的相关选项,则创建一个ErrorRequestCoordinator,将正常请求保存到primary成员中。当加载失败时,调用error的begin,如下,

笔者对失败Request不展开说,跟进buildThumbnailRequestRecursive,创建了一个ThumbnailRequestCoordinator。看类名,这是和所谓图相关的Request,跟进,

发现方法内通过obtainRequest创建了一个fullRequest,跟进,

又创建了一个SingleRequest,并且设置到ThumbnailRequestCoordinator的full成员。以及下面逻辑设置SingleRequest到thumb成员。

笔者在此总结下,通过RequestBuilder#buildRequestRecursive方法,最先创建一个ErrorRequestCoordinator,其primary成员保存ThumbnailRequestCoordinator(如果设置了加载失败的选项图),error保存错误选项ThumbnailRequestCoordinator(通过errorBuilder创建)。然后通过buildThumbnailRequestRecursive创建ThumbnailRequestCoordinator,fill成员保存通过obtainRequest创建的SingleRequest。thumb成员也是通过不同的RequestBuilder#obtainRequest创建的SingleRequest。

层层包括Request,这就用到了装饰设计模式。笔者画了以下类图供读者理解。

笔者回到主线,从RequestManager#track出发,

笔者假设没有paused,且知道这一个request是ErrorRequestCoordinator,跟进,

当primary没有运行,调用到ThumbnailRequestCoordinator#begin,跟进,

第一次调用thumb#begin,先请求缩略图,随后调用full#begin,请求完整请求。thumb和full均是SingleRequest,跟进。

model为null,非合理源,回调onLoadFailed,

如果正在running,抛出异常;如果已经完成,直接回调onResourcReady,数据源传入MEMORY_CACHE,毕竟确实在内存中(只有开启内存才会有此调用)。

笔者继续跟进,

onSizeReady,传入剪裁大小,最后调用onLoadStart,获取到预览图,通知Target。

笔者从注释知onSizeReady开始进入异步,因此进入核心方法onSizeReady,

将状态标记为RUNNING,调用engine.load方法,这是什么呢?通过该类成员猜测,这是干具体事的类。

Engine存在如cache(LRU),activeResources等缓存成员,解码decodeJobFactory等。关于该类具体工作,笔者借下文讲解。

4,Engine#load

通过load方法,传入资源model,宽高,返回类型transcodeClass,一些选项等,如是否开启缓存,以及callbackExecutor。随后通过KeyFactory生成key值,这个笔者推测与缓存相关。

跟进loadFromMemory,

如果开启跳过缓存,直接返回null,

先从ActiveResources中获取,如果没有再从Cache中获取,如果没有返回null,

我们看下loadFromActiveResourcesResources,

如果获取到不为null的值,调用acquire引用计算+1。再看下loadFromCahce方法,

从cache中获取(注意,直接从cache中remove),如果获取到引用计数+1,并且放入ActiveResources(这里面的资源计数一定大于0,表示正在被使用)中。

这里的cache是什么呢?答案是默认LruResourceCache,可以通过Glide.Builder#setMemoryCache定义用户自己的缓存,在此不赘述。

笔者注意到Glide对资源管理采用引用计数的方式,因此看下计数到0时发生什么?

调用listener#onResourceRelease,跟进看下实现,

从ActiveResources中移除,并且如果资源可缓存,存放入cache中,否则recycle掉。

笔者回到主线,如果没有缓存,调用到waitForExistingOrStartNewJob方法,

跟进,

创建一个引擎Job,创建一个解码Job

加入到jobs中国。调用start方法,注意到addCallback传入了回调和回调线程。

跟进start,

获得一个GlideExecutor,根据策略决定用哪个,笔者直接跟进DecodeJob的run方法,

根据runReason有三种状态执行,如初始化,切换资源服务,解码。第一次进入现初始化,调用runGenerators,

做了些初始化工作,调用startNext方法,generator有三个实现,如下,

至于获得哪一个,得从策略决定,

更具体的笔者暂不深跟,只要知道这里面就会请求到网络或磁盘即可。当资源准备然后切换到SWITHC_TO_SOURCE_SERVICE来解析资源,

解完成后,

切换回调线程(一般是主线程),通知资源调用完毕。

层层回调到SingleRequset#onResourceReady方法,进一步调用到Target的onResourceReady方法,

即完成了一次请求。如果是ImageVIewTarget,则进一步调用ImageView#setDrawalbe或setBitmap方法。

笔者跟到这,再回溯一下内存缓存。

如果再次进行请求,并且开启了内存缓存,怎么走呢?

由于target已存在内存,同一个request调用时,直接通过previous.begin调用,由于上次request加载完毕,因此,我们就通过内存缓存的形式返回了。

四,裁剪相关

为了防止加载大图片导致内存溢出,glide提供了裁剪功能,即传入指定宽高或sizeMultiplier

,可避免加载原图大小而崩溃,

在解码时,会根据width、height去解码,因此可以避免大图片问题崩溃。

五,恢复/暂停加载

在RequestManager中,由于监听了lifeCycler,

1,pause

笔者假设用户未设置clearOnStop,进入pauseRequests,笔者以SingleQuest为例,

简单cancel,如果有release,则释放。

clearOnStop如果为true,就清空所有的Target,间接调用到Request#clear方法,

2,resume

如下图,仍调用到Request#begin,不赘述。

六,低内存释放缓存

在Application的onLowMemory回调中,写入此方法,

或读者可自定义一个MemoryCache,内部采用SoftRefenrce方式设置到glide中。

我们看下clearMemory实现,

这三个缓存池全局单例,通过引用方式传入到Engine(Engine也是全局单例,Request所使用到的Engine来自GlideContext)。

另外,笔者提醒读者注意,只清除memroyCache、bitmapPool、arrayPool的缓存,ActiveResources不会清除。

笔者在此提问,Glide默认的缓存大小是多少呢?有兴趣的读者可以阅读MemorySizeCalculator,glide会根据当前设备ram是否是低内存ram,以及屏幕宽高动态计算出一个合适的大小。

七,三级缓存

所谓glide提供了哪三级缓存?根据核心流程的源码走读,我们看到了内存缓存。

glide提供内存缓存,磁盘缓存(持久化到文件系统中),网络缓存这三种。

内存缓存上文已经介绍过,我们接下来介绍下磁盘缓存入口,

在DecodeJob#notifyEncodeAndRelease处,

glide使用了DiskLruCache来存储,我们跟进,

如果current不为null,表示缓存存在,直接返回,不然通过editor写入到磁盘。

笔者下面介绍网络获取相关,

当需要从网络中获取资源时,glide会调用HttpUrlFetcher类,

连接后,如果状态ok,拿到InputStream,通过getStreamForSuccessfulRequest发送处url Connect,并且获取inputStream,

最后解析stream,存储LRUResoureCache中。

笔者粗陋地理解所谓的网络缓存要么是缓存在网络层,要么是从网络中解析的数据到了内存缓存中。

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

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

相关文章

Vue(二十):ElementUI 扩展实现表格组件的拖拽行

效果 源码 注意&#xff1a; 表格组件必须添加 row-key 属性&#xff0c;用来优化表格的渲染 <template><el-row :gutter"10"><el-col :span"12"><el-card class"card"><el-scrollbar><span>注意: 表格组件…

自动化测试再升级,大模型与软件测试相结合

近年来&#xff0c;软件行业一直在迅速发展&#xff0c;为了保证软件质量和提高效率&#xff0c;软件测试领域也在不断演进。如今&#xff0c;大模型技术的崛起为软件测试带来了前所未有的智能化浪潮。 软件测试一直是确保软件质量的关键环节&#xff0c;但传统的手动测试方法存…

编写交互式 Shell 脚本

在日常的系统管理和自动化任务中&#xff0c;使用 Shell 脚本可以为我们节省大量时间和精力。 文章将以输入 IP 为例&#xff0c;通过几个版本逐步完善一个案例。 原始需求 编写一个交互式的 Shell 脚本&#xff0c;运行时让用户可以输入IP地址&#xff0c;并且脚本会将输入…

国辰智企TMS定制化模块,实现智慧园区的全面管理

智慧园区综合管理系统是一种针对园区业务场景的高度定制化解决方案&#xff0c;通过选择性部署相应的模块&#xff0c;实现对园区各方面业务的全面管理。通常情况下&#xff0c;园区都需要有效地管理资产、确保安全&#xff0c;以及进行访客预约。这一全面性的系统通过各个模块…

windows 谷歌浏览器Chrome 怎么禁止更新

1.首先把任务管理器里的谷歌浏览器程序结束&#xff1a; &#xff08;鼠标在任务栏右击&#xff0c;出现任务管理器&#xff09; 2.windowr&#xff0c;输入services.msc 带有Google Update的服务&#xff0c;选择禁用。 3.windowr&#xff0c;输入taskschd.msc 任务计划程序…

二叉搜索树,力扣

目录 题目地址&#xff1a; 题目&#xff1a; 我们直接看题解吧&#xff1a; 解题分析&#xff1a; 解题思路&#xff1a; 代码实现&#xff1a; 代码补充说明&#xff1a; 代码实现(中序遍历)&#xff1a; 题目地址&#xff1a; 98. 验证二叉搜索树 - 力扣&#xff08;LeetCod…

delete、truncate和drop区别

一、从执行速度上来说 drop > truncate >> DELETE 二、从原理上讲 1、DELETE DELETE from TABLE_NAME where xxx1.1、DELETE属于数据库DML操作语言&#xff0c;只删除数据不删除表的结构&#xff0c;会走事务&#xff0c;执行时会触发trigger&#xff08; 触发器…

8. 字符串转换整数 (atoi)-LeetCode(Java)

8. 字符串转换整数 (atoi) 题目&#xff1a;8. 字符串转换整数 (atoi) 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数&#xff09;。 函数 myAtoi(string s) 的算法如下&#xff1a; 读入…

AI大语言模型学习笔记之三:协同深度学习的黑魔法 - GPU与Transformer模型

Transformer模型的崛起标志着人类在自然语言处理&#xff08;NLP&#xff09;和其他序列建模任务中取得了显著的突破性进展&#xff0c;而这一成就离不开GPU&#xff08;图形处理单元&#xff09;在深度学习中的高效率协同计算和处理。 Transformer模型是由Vaswani等人在2017年…

2024美赛预测算法 | 回归预测 | Matlab基于WOA-LSSVM鲸鱼算法优化最小二乘支持向量机的数据多输入单输出回归预测

2024美赛预测算法 | 回归预测 | Matlab基于WOA-LSSVM鲸鱼算法优化最小二乘支持向量机的数据多输入单输出回归预测 目录 2024美赛预测算法 | 回归预测 | Matlab基于WOA-LSSVM鲸鱼算法优化最小二乘支持向量机的数据多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果…

Git 指令

Git 安装 操作 命令行 简介&#xff1a; Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。 Git 与常用的版本控制工具 CVS, Subversion …

2024PMP考试新考纲-【业务环境领域】典型真题和很详细解析(2)

华研荟继续分享【业务环境Business Environment领域】在新考纲下的真题&#xff0c;帮助大家体会和理解新考纲下PMP的考试特点和如何应用所学的知识和常识&#xff08;经验&#xff09;来解题&#xff0c;并且举一反三&#xff0c;一次性3A通过2024年PMP考试。 2024年PMP考试新…

准确率90%+!大模型会话洞察平台来了

随着客户行为和需求加速改变&#xff0c;企业与客户在数字渠道沟通并交易的比重大幅提升。企业通过在线客服、社交媒体、短信、语音助手等数字化渠道与客户建立联系的方式&#xff0c;不仅拓宽了沟通途径&#xff0c;更显著提高了服务效率和质量。 与此同时&#xff0c;数字化…

POSIX(包含程序的可移植性) -- 详解

1. 什么是 POSIX 参考链接–知乎 POSIX 标准包含了进程管理、文件管理、网络通信、线程和同步、信号处理等方面的功能。 这些接口定义了函数、数据类型和常量等&#xff0c;为开发者提供了一个可移植的方法来与操作系统进行交互。 2. 谁遵守这个标准 遵守 POSIX 标准的主要是…

蛋氨酸市场调研:预计2029年将达到87亿美元

蛋氨酸&#xff0c;又名甲硫氨酸&#xff0c;化学名称为γ-甲硫基-a-氨基丁酸&#xff0c;是一种参与蛋白质合成的基本结构单位&#xff0c;是人体内八种必需氨基酸之一&#xff0c;同时也是重要的饲料添加剂。蛋氨酸主要用于家禽、猪的饲料添加剂以及药用等。对禽类来说&#…

adb脚本操作

用荣耀80手机测试 echo off setlocal enabledelayedexpansion adb shell am start com.android.settings timeout /t 2 /nobreak >nul adb shell input tap 500 1300 timeout /t 2 /nobreak >nul adb shell input tap 500 800 timeout /t 2 /nobreak >nul adb she…

ElasticSearch搜索与分析引擎-Linux离线环境安装教程

目录 一、下载安装包 网盘链接: 二、安装流程及遇到的问题和解决方案 &#xff08;1&#xff09;JDK安装 &#xff08;2&#xff09;Elasticsearch安装 &#xff08;3&#xff09;Kibana安装 ​&#xff08;4&#xff09;Ik分词器安装 三、启动过程中的问题 &#xff…

linux查看mysql状态重启

1.linux怎么看mysql数据库是不是宕机了&#xff1f; MySQL/MariaDB数据库的状态&#xff1a;使用systemctl status mysql或者service mysqld status命令。如果显示"active (running)"表示MySQL正常运行&#xff1b;如果显示"inactive (dead)"则表示MySQL已…

C语言系列-联合

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” 目录 联合体 联合体类型的声明 联合体的特点 相同成员的结构体和联合体对比 联合体大小的计算 联合的一个练习 联合体 联合体类型的声明 像结构体一样&#xff0c;联合体也是由…

NLP深入学习(十二):支持向量机(SVM)

文章目录 0. 引言1. 什么是支持向量机2. 线性 SVM3. 非线性 SVM3.1 推导过程3.2 常用核函数 4. 参考 0. 引言 前情提要&#xff1a; 《NLP深入学习&#xff08;一&#xff09;&#xff1a;jieba 工具包介绍》 《NLP深入学习&#xff08;二&#xff09;&#xff1a;nltk 工具包…