一直想写一个篇 Glide原理讲解,可一直没能动笔。
不是因为没时间,是 Glide 涉及太过广泛,内部逻辑太过犀利。一直没能找到一个合适的制高点来俯览全身。
之前对 Glide 一直停留在使用层面,只晓得使用方便,链式调用,节约内存,无所顾及内部原理,更没能学习其架构设计。
接下来我将通过以下几个问题来引伸分析Glide的内部逻辑,尽量做到 由使用到原理,从架构到逻辑,深入浅出。
- Glide是干啥的?
- 为什么用Glide?
- Glide怎么用?
- Glide包含哪些模块?都干什么用的?
- Glide调用某一个方法后具体干了什么事?
本文基于Glide的最新版本 4.10.0 进行分析,如有错误欢迎指出。
注:最新版本是4.11.0,发晚了,但是4.10.0 也算是非常非常新的了。
960页全网最全Android开发笔记:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题等等,帮助大家深刻理解Android相关知识点的原理以及面试相关知识。
1. 请用一脸懵逼的姿势了解,Glide到底是干啥的?
Glide is a fast and efficient open source media management and image loading framework for Android that wraps media decoding, memory and disk caching, and resource pooling into a simple and easy to use interface.
翻译成能听懂的
Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。
2. 那你为什么要用Glide?
有个不知道哪的人,不知道叫什么名的人曾经说过,当你开始问 为什么 的时候,那么你可能要变身了。
这个问题非常简洁,但分析透彻实属不易,甚至该问题是伴随使用的需求,场景变化而变化的。既然不好直观的分析说明,我们就罗列对应优势,方便需要的时候对号入座
- 优雅的调用姿 Glide.with(fragment).load(url).into(imageView);
- 多维度的图片格式(png,jpg,gif,webp,video等...)
- 健壮的身体素质 性能极好,能防止频繁主线程I/O,垃圾回收而导致的页面闪烁卡顿等。
- 灵活的加载请求 可根据页面的生命周期动态管理图片的加载请求。
- 风骚的缓存策略 支持按控件大小缓存对应大小的图片,且默认的图片格式为RGB_565,相对占用空间更小。速度也因此更快。
嗯~~~ 就看这几个,你说你要不要选Glide.....
3. Glide怎么用?
可算是到我闭着眼都能写的环节了,来来来,咱们撸代码。
首先增加如下引用。
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
使用方式,我们罗列5种常用的使用方式:
//********************************** 直接使用 **********************************
Glide.with(Context).load("").into(mImageView)
//使用Generated API, 作用范围Application 模块内使用
//创建MyAppGlideModule类加上@GlideModule注解,buid后既能使用 GlideApp
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}
//Generated API加载图片
GlideApp.with(Context).load("").into(mImageView);
//********************************** 添加占位图 **********************************
RequestOptions requestOptions = new RequestOptions()
.placeholder(R.drawable.a)
.error(R.drawable.e)
.diskCacheStrategy(DiskCacheStrategy.NONE);//设置缓存了参数
Glide.with(Context).load("").apply(requestOptions).into(mImageView);
//Generated API 方式(和Glide3 一样)
GlideApp.with(Context).load("")
.placeholder(R.drawable.a)
.error(R.drawable.e)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(mImageView);
// 后备回调符(Fallback) Generated API 方式才有
//在设置图片的场景中,如果用户不设置,也就是为null的情况,可使用后备回调符显示默认图
private static final String NULL_URL=null;
GlideApp.with(Context).load(NULL_URL)
.fallback(R.drawable.a)
.into(mImageView);
//********************************** 指定加载图片的大小(override) **********************************
RequestOptions requestOptions = new RequestOptions().override(200,100);
Glide.with(Context).load("").apply(requestOptions).into(mImageView);
//Generated API 方式
GlideApp.with(Context).load("")
.override(200,100)
.into(mImageView);
//********************************** 通过thumbnail加载缩略图 **********************************
//与placeholder类似,但是thumbnail可加载网络图,placeholder只能加载本地图。
RequestOptions requestOptions = new RequestOptions()
.override(200,100)
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(MainActivity.this)
.load(url)
.thumbnail( Glide.with(this)
.load(url)
.apply(requestOptions))
.into(iv);
//Generated API 方式
GlideApp.with(Context)
.load(url)
.thumbnail( GlideApp.with(this)
.load(IMAGE_URL).override(200,100)
.diskCacheStrategy(DiskCacheStrategy.NONE))
.into(mImageView);
//********************************** 设置图片的变化操作 **********************************
//可通过
//CenterCrop(图片原图的中心区域进行裁剪显示)
//FitCenter(图片原始长宽铺满)
//CircleCrop(圆形裁剪)
//设置图片的变化操作
Glide.with(Context)
.load(IMAGE_URL)
.apply(RequestOptions.circleCropTransform())
.into(mImageView);
//Generated API 方式
GlideApp.with(Context).load(IMAGE_URL)
.circleCrop()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(mImageView);
4. Glide包含哪些模块?都干什么用的?
接下来的环节将是本篇文章的高地。
我们可以先简单思考下,如果你是Glide的作者,你要怎么设计一个优秀的图片加载框架,需要兼顾哪些环节,需要包含哪些模块?
简单来说,图片加载需要以下几种模块。
- 参数封装
- 加载请求
- 执行引擎
- 数据加载器
- 解码器
- 编码器
- 缓存
那实际Glide是这样设计的么?
- 参数封装(获得请求的参数,配置加载图的属性)
- 网络请求(下载图片)
- 缓存逻辑(资源重用)
- 加解码(处理图片)
- 数据加载(判断加载方式/请求网络/IO读取/内存读取)
- 线程池/线程队列(或者叫任务队列,用于处理每次的加载任务)
- 防止OOM的处理机制(软引用,缓存,压缩,存储等..)
- 生命周期管理(防止内存泄露)
- .......
说了这么多,那加载一张图片,到底是怎样一个流程呢?换句话说,这些模块具体是怎样协同工作的,调用顺序又是怎样的呢?
给大家画了一个简单的图,兄弟们,这个图来之不易啊,你不从头捋一遍你真是不知道咋画啊...
好多小伙伴可能看到这个图,又懵逼了, 你这都啥跟啥啊,一个Glide你画这么几个块就over了?
那网络咋加载的?那缓存怎么处理的,那线程咋切换的?那生命周期咋控制的。啥也没说啊..
各位,各位,先别着急。咱们力争的是先 整体再局部,先总览再细分。接下来咱们针对代码进行跟进。
兄台准备好面对疾风了么?
5. Glide调用某一个方法后具体干了什么事?
首先我们拿一个简单的使用进行跟进
Glide.with(context).load("").into(mImageView)
链式调用,我们分解为以下三个方法进行分析。
Glide.with(context)
Glide.with(context).load("")
Glide.with(context).load("").into(mImageView)
5.1. Glide.with(context)具体干了啥?
死亡如风,常伴吾身。我会给你个痛快的!
/**
* 简单来说该方法进行了以下几步操作
* 1. Glide 初始化
* 2. 各种线程池初始化,各种缓存对象初始化
* 3. Engine初始化,至于Engine是啥,后续我们慢慢介绍
* 4. RequestManager 初始化
* 5. 创建透明的fragment,动态监听生命周期
*/
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// 检测传入的context是否合规
Preconditions.checkNotNull(context, "You cannot start .....");
return Glide.get(context).getRequestManagerRetriever();
}
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
//如果glide为null则启动检测初始化逻辑
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
private static void checkAndInitializeGlide(@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
// 如果已经在初始化中了,则抛异常。
if (isInitializing) {
throw new IllegalStateException( "You cannot call Glide.get() in registerComponents(), use the provided Glide instance instead");
}
//设置初始化状态
isInitializing = true;
initializeGlide(context, generatedAppGlideModule);
isInitializing = false;
}
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
...
//调用GlideBuilder.build()方法进行Glide初始化
Glide glide = builder.build(applicationContext);
...
//注册回调
ap