[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)
- Android
- 四大组件
- Activity
- Fragment
- Service
- 序列化
- Handler
- Hander介绍
- MessageQueue详细
- 启动流程
- 系统启动流程
- 应用启动流程
- Activity启动流程
- View
- view绘制
- view事件传递
- choreographer
- LayoutInflater
- UI渲染概念
- Binder
- Binder原理
- Binder最大数据
- Binder小结
- Android组件
- ListView原理
- RecyclerView原理
- SharePreferences
- AsyncTask
- Sqlite
- SQLCipher加密
- 迁移与修复
- Sqlite内核
- Sqlite优化v2
- sqlite索引
- sqlite之wal
- sqlite之锁机制
- 网络
- 基础
- TCP
- HTTP
- HTTP1.1
- HTTP2.0
- HTTPS
- HTTP3.0
- HTTP进化图
- HTTP小结
- 实践
- 网络优化
- Json
- ProtoBuffer
- 断点续传
- 性能
- 卡顿
- 卡顿监控
- ANR
- ANR监控
- 内存
- 内存问题与优化
- 图片内存优化
- 线下内存监控
- 线上内存监控
- 启动优化
- 死锁监控
- 崩溃监控
- 包体积优化
- UI渲染优化
- UI常规优化
- I/O监控
- 电量监控
- 第三方框架
- 网络框架
- Volley
- Okhttp
- 网络框架n问
- OkHttp原理N问
- 设计模式
- EventBus
- Rxjava
- 图片
- ImageWoker
- Gilde的优化
- APT
- 依赖注入
- APT
- ARouter
- ButterKnife
- MMKV
- Jetpack
- 协程
- MVI
- Startup
- DataBinder
- 黑科技
- hook
- 运行期Java-hook技术
- 编译期hook
- ASM
- Transform增量编译
- 运行期Native-hook技术
- 热修复
- 插件化
- AAB
- Shadow
- 虚拟机
- 其他
- UI自动化
- JavaParser
- Android Line
- 编译
- 疑难杂症
- Android11滑动异常
- 方案
- 工业化
- 模块化
- 隐私合规
- 动态化
- 项目管理
- 业务启动优化
- 业务架构设计
- 性能优化case
- 性能优化-排查思路
- 性能优化-现有方案
- 登录
- 搜索
- C++
- NDK入门
- 跨平台
- H5
- Flutter
- Flutter 性能优化
- 数据跨平台