企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] # Glide做了哪些优化? 要想要回答这个问题,我们可以先想一想,如果我们自己要实现一个图片加载框架,我们会思考什么问题? 1.图片下载是个耗时过程,我们首先需要考虑的就是图片缓存的问题 2.图片加载也是个耗内存的操作,很多`OOM`都是图片加载导致的,所以我们也要考虑内存优化问题 3.图片加载到一半,页面关闭了,图片加载也应该中止,这又牵扯到了生命周期管理的问题 4.还有就是图片加载框架是否支持大图加载?大图情况下会有什么问题? 以上就是我们提出的有关于`Glide`的几个问题了,这样我们可以轻松得出本文主要包括的内容 1.`Glide`图片加载的总体流程介绍 2.`Glide`缓存机制做了哪些优化? 3.`Glide`做了哪些内存优化? 4.`Glide`如何管理生命周期? 5.`Glide`怎么做大图加载? 下面就带着问题进入正文~ ## `Glide`图片加载总体流程介绍 在开始了解`Glide`具体做了哪些优化之前,我们先对`Glide`图片加载的总体流程做一个简单的介绍,让大家首先有个整体概念。 同时在后面对`Glide`做的优化具体发生在哪一步也可以方便的知道. 概括来说,图片加载包含封装,解析,下载,解码,变换,缓存,显示等操作,如下图所示: ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/552652e7cc024d7297cfc15e9b0ae6dd~tplv-k3u1fbpfcp-watermark.awebp) * 1.封装参数:从指定来源,到输出结果,中间可能经历很多流程,所以第一件事就是封装参数,这些参数会贯穿整个过程; * 2.解析路径:图片的来源有多种,格式也不尽相同,需要规范化; * 3.读取缓存:为了减少计算,通常都会做缓存;同样的请求,从缓存中取图片(`Bitmap`)即可; * 4.查找文件/下载文件:如果是本地的文件,直接解码即可;如果是网络图片,需要先下载; * 5.解码:这一步是整个过程中最复杂的步骤之一,有不少细节; * 6.变换:解码出`Bitmap`之后,可能还需要做一些变换处理(圆角,滤镜等); * 7.缓存:得到最终bitmap之后,可以缓存起来,以便下次请求时直接取结果; * 8.显示:显示结果,可能需要做些动画(淡入动画,crossFade等)。 以上就是`Glide`图片加载的总体流程,这里只做了简单的介绍,详情可见:[聊一聊关于Glide在面试中的那些事](https://juejin.cn/post/6844904002551808013 "https://juejin.cn/post/6844904002551808013") ## `Glide`缓存机制做了哪些优化? 我们知道,下载图片是非常耗费资源的,所以图片缓存机制是图片加载框架很重要的一部分,下面就以一张表格来说明下 Glide 缓存。 | 缓存类型 | 缓存代表 | 说明 | | --- | --- | --- | | 活动缓存 | ActiveResources | 如果当前对应的图片资源是从内存缓存中获取的,那么会将这个图片存储到活动资源中。 | | 内存缓存 | LruResourceCache | 图片解析完成并最近被加载过,则放入内存中 | | 磁盘缓存-资源类型 | DiskLruCacheWrapper | 被解码后的图片写入磁盘文件中 | | 磁盘缓存-原始数据 | DiskLruCacheWrapper | 网络请求成功后将原始数据在磁盘中缓存 | 在介绍具体缓存前,先来看一张加载缓存执行顺序,有个大概的印象 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/750d9bb09f2e4e8c8b0975ec43b941fa~tplv-k3u1fbpfcp-watermark.awebp) `Glide`的缓存机制,主要分为2种缓存,一种是内存缓存,一种是磁盘缓存。 之所以使用内存缓存的原因是:防止应用重复将图片读入到内存,造成内存资源浪费。 之所以使用磁盘缓存的原因是:防止应用重复的从网络或者其他地方下载和读取数据。 正式因为有着这两种缓存的结合,才构成了`Glide`极佳的缓存效果。 ### 内存缓存 `Glide`默认开启内存缓存,我们也可以通过`skipMemoryCache`关闭 上面我们可以看到内存缓存其实分两个部分,`ActiveResource`缓存与`LRU`缓存 `ActiveResources` 就是一个弱引用的 `HashMap` ,用来缓存正在使用中的图片,使用 `ActiveResources` 来缓存正在使用中的图片,可以保护这些图片不会被 `LruCache` 算法回收掉 内存缓存加载顺序如下: 1.根据图片地址,宽高,变换,签名等生成`key` 2.第一次加载没有获取到活动缓存。 3.接着加载内存资源缓存,先清理掉内存缓存,在添加进行活动缓存。 4.第二次加载活动缓存已经存在。 5.当前图片引用为 0 的时候,清理活动资源,并且添加进内存资源。 6.又回到了第一步,然后就这样环环相扣。 总结为流程图如下: ![](https://img.kancloud.cn/56/b3/56b38ce65aec414430f71596ec933ca5_960x988.png) 这里没有贴出源码,如果想要看源码的同学可参考:[从源码的角度分析 Glide 缓存策略](https://juejin.cn/post/6844903953604280328#heading-3 "https://juejin.cn/post/6844903953604280328#heading-3") 我们上面总结了`Glide`内存缓存加载的流程,看到这里我们很容易有个疑问,为什么`Glide`要设计两种内存缓存? #### 为什么设计两种内存缓存? > `LruCache`算法的实现,你会发现它其实是用一个`LinkedHashMap`来缓存对象的,每次内存超出缓存设定的时候,就会把最近最少使用的缓存去掉,因此有可能会把正在使用的缓存给误伤了,我还在用着它呢就给移出去了。因此这个弱引用可能是对正在使用中的图片的一种保护,使用的时候先从`LruCache`里面移出去,用完了再把它重新加到缓存里面。 举个例子 ![](https://img.kancloud.cn/50/a4/50a4b00e79fa5d81a4b93b4f0d61b9fc_1105x960.png) 比如我们 `Lru` 内存缓存 `size` 设置装 99 张图片,在滑动 `RecycleView` 的时候,如果刚刚滑动到 100 张,那么就会回收掉我们已经加载出来的第一张,这个时候如果返回滑动到第一张,会重新判断是否有内存缓存,如果没有就会重新开一个 `Request` 请求,很明显这里如果清理掉了第一张图片并不是我们要的效果。所以在从内存缓存中拿到资源数据的时候就主动添加到活动资源中,并且清理掉内存缓存中的资源。这么做很显然好处是 保护不想被回收掉的图片不被 `LruCache` 算法回收掉,充分利用了资源。 #### 小结 本节主要总结了`Glide`内存缓存加载的流程 1.首先去获取活动缓存,如果加载到则直接返回,没有则进入下一步 2.接着去获取`LRU`缓存,在获取时会将其从`LRU`中删除并添加到活动缓存中 3.下次加载就可以直接加载活动缓存了 4.当图片引用为0时,会从活动缓存中清除并添加到`LRU`缓存中 5.之所以要设计两种内存缓存的原因是为了防止加载中的图片被`LRU`回收 ### 磁盘缓存 首先了解一下磁盘缓存策略 * `DiskCacheStrategy.NONE`: 表示不缓存任何内容。 * `DiskCacheStrategy.RESOURCE`: 在资源解码后将数据写入磁盘缓存,即经过缩放等转换后的图片资源。 * `DiskCacheStrategy.DATA`: 在资源解码前将原始数据写入磁盘缓存。 * `DiskCacheStrategy.ALL` : 使用`DATA`和`RESOURCE`缓存远程数据,仅使用`RESOURCE`来缓存本地数据。 * `DiskCacheStrategy.AUTOMATIC`:它会尝试对本地和远程图片使用最佳的策略。当你加载远程数据时,`AUTOMATIC` 策略仅会存储未被你的加载过程修改过的原始数据,因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,`AUTOMATIC` 策略则会仅存储变换过的缩略图,因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。默认使用这种缓存策略 在了解磁盘缓存时我们主要需要明确一个概念,是当我们使用 `Glide` 去加载一张图片的时候,`Glide` 默认并不会将原始图片展示出来,而是会对图片进行压缩和转换,总之就是经过种种一系列操作之后得到的图片,就叫转换过后的图片。 我们既可以缓存变换之前的原始图片,也可以缓存变换后的图片 ### 为什么需要两种磁盘缓存 上文已经说了,`DiskCacheStrategy.RESOURCE`缓存的是变换后的资源,`DiskCacheStrategy.DATA`缓存的是变换前的资源 举个例子,同一张图片,我们先在`100*100`的`View`是展示,再在`200*200`的`View`上展示 如果不缓存变换后的类型相当于每次都要进行一次变换操作,如果不缓存原始数据则每次都要去重新下载数据 如下可以看出,两种缓存的`key`不一样 ~~~java DiskCacheStrategy.RESOURCE currentKey = new ResourceCacheKey(helper.getArrayPool(),sourceId,helper.getSignature(),helper.getWidth(),helper.getHeight(),transformation,resourceClass,helper.getOptions()); DiskCacheStrategy.DATA DataCacheKey newOriginalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature()); ~~~ ### 小结 本节主要介绍了`Glide`磁盘缓存的几种策略并介绍了为什么需要两种磁盘缓存的原因 这里也没有贴什么源码,如果想要看源码的同学可参考:[从源码的角度分析 Glide 缓存策略](https://juejin.cn/post/6844903953604280328#heading-8 "https://juejin.cn/post/6844903953604280328#heading-8") ## `Glide`做了哪些内存优化? `Glide`的内存优化主要也是对`Bitmap`的优化,在回答这个问题前,我们可以想想有哪些常见的`Bitmap`优化手段 1.当图片大小与`View`大小不一致时,可以用`inSampleSize`进行尺寸优化 2.图片所占内存即宽*高*每像素所占内存大小,不同的模式每个像素所占的内存大小不同,我们可以利用`inpreferredconfig`配置 3.`Bitmpa`所占内存比较大,如果频繁创建回收`Bitmap`内存可能造成内存抖动,我们可以利用`inBitmap`利用`Bitmap`内存 4.内存缓存,上文我们已经介绍了`Glide`的弱引用缓存与`LRU`缓存 其实常见的`Bitmap`内存优化也就这么几种了,不过我们在工作中比较少直接使用他们。 下面我们就介绍下`Glide`中具体是怎么使用他们的. ### 尺寸优化 当装载图片的容器例如ImageView只有`100*100`,而图片的分辨率为`800 * 800`,这个时候将图片直接放置在容器上,很容易`OOM`,同时也是对图片和内存资源的一种浪费。当容器的宽高都很小于图片的宽高,其实就需要对图片进行尺寸上的压缩,将图片的分辨率调整为`ImageView`宽高的大小,一方面不会对图片的质量有影响,同时也可以很大程度上减少内存的占用 我们通常使用`inSampleSize`对`Bitmap`进行尺寸缩放 > 如果`inSampleSize` 设置的值大于1,则请求解码器对原始的`bitmap`进行子采样图像,然后返回较小的图片来减少内存的占用,例如`inSampleSize` == 4,则采样后的图像宽高为原图像的1/4,而像素值为原图的1/16,也就是说采样后的图像所占内存也为原图所占内存的1/16;当`inSampleSize` <=1时,就当作1来处理也就是和原图一样大小。另外最后一句还注明,`inSampleSize`的值一直为2的幂,如1,2,4,8。任何其他的值也都是四舍五入到最接近2的幂。 ~~~java //1 int widthScaleFactor = orientedSourceWidth / outWidth; int heightScaleFactor = orientedSourceHeight / outHeight; //2 int scaleFactor = rounding == SampleSizeRounding.MEMORY ? Math.max(widthScaleFactor, heightScaleFactor) : Math.min(widthScaleFactor, heightScaleFactor); int powerOfTwoSampleSize; //3 if (Build.VERSION.SDK_INT <= 23 && NO_DOWNSAMPLE_PRE_N_MIME_TYPES.contains(options.outMimeType)) { powerOfTwoSampleSize = 1; } else { //4 powerOfTwoSampleSize = Math.max(1, Integer.highestOneBit(scaleFactor)); //5 if (rounding == SampleSizeRounding.MEMORY // exactScaleFactor由各个裁剪策略如CenterCrop重写得到,详情可见代码 && powerOfTwoSampleSize < (1.f / exactScaleFactor)) { powerOfTwoSampleSize = powerOfTwoSampleSize << 1; } } options.inSampleSize = powerOfTwoSampleSize; ~~~ 如上就是`Glide`图片进行尺寸缩放相关的代码 1.首先计算出图片与`View`的宽高比 2.根据缩放策略是省内存还是高品质,决定取宽高比的最大值还是最小值 3.当`Build.VERSION.SDK_INT<=23`时,一些格式的图片不能缩放 4.`highestOneBit`的功能是把我们计算的比例四舍五入到最接近2的幂 5.如果缩放策略为省内存,并且我们计算的`SampleSize<exactScaleFactor`,将`inSampleSize*2` 如上就是`Glide`图片加载时做尺寸优化的大概逻辑 ### 图片格式优化 我们知道,`Bitmap`所占内存大小,由`宽*高*每像素所占内存`决定 上面的尺寸优化决定宽高,图片格式优化决定每像素所占内存 在`API29`中,将`Bitmap`分为`ALPHA_8`, `RGB_565`, `ARGB_4444`, `ARGB_8888`, `RGBA_F16`, `HARDWARE`六个等级。 * `ALPHA_8`:不存储颜色信息,每个像素占1个字节; * `RGB_565`:仅存储`RGB`通道,每个像素占2个字节,对`Bitmap`色彩没有高要求,可以使用该模式; * `ARGB_4444`:已弃用,用`ARGB_8888`代替; * `ARGB_8888`:每个像素占用4个字节,保持高质量的色彩保真度,默认使用该模式; * `RGBA_F16`:每个像素占用8个字节,适合宽色域和`HDR`; * `HARDWARE`:一种特殊的配置,减少了内存占用同时也加快了`Bitmap`的绘制。 每个等级每个像素所占用的字节也都不一样,所存储的色彩信息也不同。同一张100像素的图片,`ARGB_8888`就占了400字节,`RGB_565`才占200字节,RGB\_565在内存上取得了优势,但是`Bitmap`的色彩值以及清晰度却不如`ARGB_8888`模式下的`Bitmap` 值得注意的是在`Glide4.0`之前,`Glide`默认使用`RGB565`格式,比较省内存 但是`Glide4.0`之后,默认格式已经变成了`ARGB_8888`格式了,这一优势也就不存在了。 这本身也就是质量与内存之间的取舍,如果应用所需图片的质量要求不高,也可以修改默认格式 ~~~java //默认格式修改为了ARGB_8888 public static final Option<DecodeFormat> DECODE_FORMAT = Option.memory( "com.bumptech.glide.load.resource.bitmap.Downsampler.DecodeFormat", DecodeFormat.DEFAULT); ~~~ ### 内存复用优化 `Bitmap`所占内存比较大,如果我们频繁创建与回收`Bitmap`,那么很容易造成内存抖动,所以我们应该尽量复用`Bitmap`内存 `Glide`主要使用了`inBitmap`与`BitmapPool`来实现内存的复用 #### `inBitmap`介绍 在 `Android 3.0(API 级别 11)`开始,系统引入了 `BitmapFactory.Options.inBitmap` 字段。如果设置了此选项,那么采用 `Options` 对象的解码方法会在生成目标 `Bitmap` 时尝试复用 `inBitmap`,这意味着 `inBitmap` 的内存得到了重复使用,从而提高了性能,同时移除了内存分配和取消分配。不过 `inBitmap` 的使用方式存在某些限制,在 `Android 4.4(API 级别 19)`之前系统仅支持复用大小相同的位图,4.4 之后只要 `inBitmap` 的大小比目标 `Bitmap` 大即可 #### `BitmapPool`介绍 通过上文我们知道了可以通过`inBitmap`复用内存,但是还需要一个地方存储可复用的`Bitmap`,这就是`BitmapPool` `JDK` 中的 `ThreadPoolExecutor` 相信大多数开发者都很熟悉,我们一般将之称为“线程池”。池化是一个很常见的概念,其目的都是为了实现对象复用,例如 `ThreadPoolExecutor` 就实现了线程的复用机制 `BitmapPool`即实现了`Bitmap`的池化 #### `Glide`的应用 ~~~java private static void setInBitmap( BitmapFactory.Options options, BitmapPool bitmapPool, int width, int height) { @Nullable Bitmap.Config expectedConfig = null; if (expectedConfig == null) { expectedConfig = options.inPreferredConfig; } // BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe. options.inBitmap = bitmapPool.getDirty(width, height, expectedConfig); } ~~~ 如上即是`Glide`设置`inBitmap`的代码,向`BitmapPool`中传入宽高与格式,得到一个可复用的对象,这样就实现了`Bitmap`的内存复用 由于篇幅原因,详细的源码这里没有贴出来,想要了解更多的读者可参考:[Coil 和 Glide 的 Bitmap 缓存复用机制](https://juejin.cn/post/6956090846470995975 "https://juejin.cn/post/6956090846470995975") ## `Glide`如何管理生命周期? 当我们在做一个网络请示时,页面退出时应该中止请示,不然容易造成内存泄漏 对于图片加载也是如此,我们在页面退出时应该中止请示,销毁资源。 但是我们使用`Glide`的时候却不需要在页面退出时做什么操作,说明`Glide`可以做到在页面关闭时自动释放资源 下面我们一起看下`Glide`是如何实现的 主要是两步: 1.调用时通过`Glide.with`传入`context`,利用`context`构建一个`Fragment` 2.监听`Fragment`生命周期,销毁时释放`Glide`资源 ### 传入`context`构建`Fragment` ~~~java //通过Activity拿到RequestManager public RequestManager get(@NonNull Activity activity) { //拿到当前Activity的FragmentManager android.app.FragmentManager fm = activity.getFragmentManager(); //生成一个Fragment去绑定一个请求管理RequestManager return fragmentGet( activity, fm, /*parentHint=*/ null, isActivityVisible(activity)); } private RequestManager fragmentGet(@NonNull Context context, @NonNull android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint, boolean isParentVisible) { //①在当前Activity添加一个Fragment用于管理请求的生命周期 RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible); //获取RequestManager RequestManager requestManager = current.getRequestManager(); //如果不存在RequestManager,则创建 if (requestManager == null) { Glide glide = Glide.get(context); //②构建RequestManager //current.getGlideLifecycle()就是ActivityFragmentLifecycle,也就是构建RequestManager时会传入fragment中的ActivityFragmentLifecycle requestManager = factory.build( glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context); //将构建出来的RequestManager绑定到fragment中 current.setRequestManager(requestManager); } //返回当前请求的管理者 return requestManager; } 复制代码 ~~~ 如上所示: 1.在当前`Activity`添加一个透明`Fragment`用于管理请示生命周期 2.构建`RequestManager`并传入`Fragment`生命周期 ### `RequestManager`监听生命周期 ~~~java public class RequestManager implements LifecycleListener, ModelTypes<RequestBuilder<Drawable>> { RequestManager( Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory, Context context) { ... //将当前对象注册到ActivityFragmentLifecycle lifecycle.addListener(this); } //... //RequestManager实现了fragment生命周期回调 @Override public synchronized void onStart() { resumeRequests(); targetTracker.onStart(); } @Override public synchronized void onStop() { pauseRequests(); targetTracker.onStop(); } @Override public synchronized void onDestroy() { targetTracker.onDestroy(); } } public class RequestManagerFragment extends Fragment { //生命周期的关键就在ActivityFragmentLifecycle private final ActivityFragmentLifecycle lifecycle; public RequestManagerFragment() { this(new ActivityFragmentLifecycle()); } RequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) { this.lifecycle = lifecycle; } @Override public void onStart() { super.onStart(); lifecycle.onStart(); } @Override public void onStop() { super.onStop(); lifecycle.onStop(); } @Override public void onDestroy() { super.onDestroy(); lifecycle.onDestroy(); unregisterFragmentWithRoot(); } //... } 复制代码 ~~~ 逻辑很简单:`Fragment`生命周期变化会回调`RequestManager`生命周期,然后在进行相关的资源释放工作 ### 小结 ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/926e97d193d14bb88184079b4932dc86~tplv-k3u1fbpfcp-watermark.awebp) `Glide.with(this)`绑定了`Activity`的生命周期。在`Activity`内新建了一个无`UI`的`Fragment`,这个`Fragment`持有一个`Lifecycle`,通过`Lifecycle`在`Fragment`关键生命周期通知`RequestManager进`行相关从操作。在生命周期`onStart`时继续加载,`onStop`时暂停加载,`onDestory`时停止加载任务和清除操作。 由于篇幅有限,这里没有贴太多代码,更多细节可参考:[Glide生命周期管理](https://link.juejin.cn?target=https%3A%2F%2Fwww.jianshu.com%2Fp%2F190285e18ae1 "https://www.jianshu.com/p/190285e18ae1") ## `Glide`怎么做大图加载 对于图片加载还有种情况,就是单个图片非常巨大,并且还不允许压缩。比如显示:世界地图、清明上河图、微博长图等 首先不压缩,按照原图尺寸加载,那么屏幕肯定是不够大的,并且考虑到内存的情况,不可能一次性整图加载到内存中 所以这种情况的优化思路一般是局部加载,通过`BitmapRegionDecoder`来实现 这种情况下通常`Glide`只负责将图片下载下来,图片的加载由我们自定义的`ImageView`来实现 ### `BitmapRegionDecoder`介绍 `BitmapRegionDecoder`主要用于显示图片的某一块矩形区域,如果你需要显示某个图片的指定区域,那么这个类非常合适。 对于该类的用法,非常简单,既然是显示图片的某一块区域,那么至少只需要一个方法去设置图片;一个方法传入显示的区域即可 举个例子: ~~~java //设置显示图片的中心区域 BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; Bitmap bitmap = bitmapRegionDecoder.decodeRegion(new Rect(width / 2 - 100, height / 2 - 100, width / 2 + 100, height / 2 + 100), options); mImageView.setImageBitmap(bitmap); 复制代码 ~~~ 更详细的实现可见:[Android 高清加载巨图方案 拒绝压缩图片](https://link.juejin.cn?target=https%3A%2F%2Fblog.csdn.net%2Flmj623565791%2Farticle%2Fdetails%2F49300989%2F "https://blog.csdn.net/lmj623565791/article/details/49300989/") 不过这种方法虽然也能加载大图,但做的还不够,滑动时内存抖动,卡顿现象比较明显,不能用于线上 ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a433a5e33dfb47b8a60a76eebeb11e13~tplv-k3u1fbpfcp-watermark.awebp) 下面介绍一种可以用于线上的大图加载方案 ## 可用于线上的大图加载方案 介绍一个开源库:[subsampling-scale-image-view](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fdavemorrissey%2Fsubsampling-scale-image-view "https://github.com/davemorrissey/subsampling-scale-image-view") `SubsamplingScaleImageView`将大图切片,再判断是否可见,如果可见则加入内存中,否则回收,减少了内存占用与抖动 同时根据不同的缩放比例选择合适的采样率,进一步减少内存占用 同时在子线程进行decodeRegion操作,解码成功后回调至主线程,减少UI卡顿. 之前我也做`BitmapRegionDecoder`与`SubsamplingScaleImageView`的内存分析 有兴趣的同学也可以了解下:[Android性能优化之UI卡顿优化实例分析](https://juejin.cn/post/6870389004387385352 "https://juejin.cn/post/6870389004387385352") ## 总结 本文主要以`Glide`做了哪些优化为切入点,回答了如下几个问题 1.说一下`Glide`图片加载的总体流程 2.`Glide`缓存机制做了哪些优化? 3.`Glide`做了哪些内存优化? 4.`Glide`如何管理生命周期? 5.`Glide`怎么做大图加载? 如果对您有所帮助,欢迎点赞,谢谢~ # 参考资料 [【带着问题学】Glide做了哪些优化?](https://juejin.cn/post/6970683481127043085)