Glide
什么是Glide?
Glide 是Android开源控件,是 Android 在开发中广泛使用的图片加载和缓存库。
为什么用Glide?
因为Glide简化 Android 应用中的图片加载过程,提供高效、流畅的用户体验。以下是 Android Glide 的一些主要特点和优势:
主要特点:
1.简单易用:
Glide 提供了直观且易于使用的 API,使得开发者能够快速地加载和显示图片,而无需担心复杂的图片处理逻辑。
例如:
1.1、不用Glide可能需要这样加载图片
// Kotlin 代码示例:不使用 Glide
// 假设你有一个 ImageView 和一个图片的 URL
val imageView: ImageView = findViewById(R.id.imageView)
val imageUrl: String = "http://example.com/image.jpg"
// 使用 AsyncTask 来异步加载图片
AsyncTask.execute(Runnable {
// 在后台线程中下载图片
val imageBitmap: Bitmap? = downloadImage(imageUrl)
// 将结果切换回 UI 线程
runOnUiThread {
// 在 UI 线程中设置 ImageView 的图片
imageView.setImageBitmap(imageBitmap)
}
})
// 模拟的图片下载函数,实际开发中应使用合适的网络库如 OkHttp, Retrofit 等
private fun downloadImage(url: String): Bitmap? {
// 实现图片下载和解码的逻辑
// 这里省略了实际的下载代码
return null // 返回下载并解码后的 Bitmap,或者为 null 表示下载失败
}
2、使用Glide加载图片
// Kotlin 代码示例:使用 Glide
// 假设你有一个 ImageView 和一个图片的 URL
val imageView: ImageView = findViewById(R.id.imageView)
val imageUrl: String = "http://example.com/image.jpg"
// 使用 Glide 加载图片
Glide.with(this)
.load(imageUrl)
.into(imageView)
// 无需关心下载、解码和缓存的细节,Glide 会自动处理
从上述两个例子中可以看出,相比之下,使用 Glide 大大简化了代码,仅仅三行则可以。你不需要担心线程管理、图片解码或缓存。
1.2、使用Glide加载图片
//使用 Glide
// 假设你有一个 ImageView 和一个图片的 URL
val imageView: ImageView = findViewById(R.id.imageView)
val imageUrl: String = "http://example.com/image.jpg"
// 使用 Glide 加载图片
Glide.with(this)
.load(imageUrl)
.into(imageView)
// 无需关心下载、解码和缓存的细节,Glide 会自动处理
从上述两个例子中可以看出,相比之下,使用 Glide 大大简化了代码,仅仅三行则可以加载出图片到View中。而且不需要担心线程管理、图片解码或缓存。
** ## 2.高效性能 :
以下我会从几方面解释Glide为什么高效
1 异步加载:
Glide 使用后台线程异步加载图片,从而避免阻塞 UI 线程。这确保了应用的流畅性和响应性,尤其是在加载大量图片或处理大图时。
2 缓存管理:
内置了强大的缓存机制,包括内存缓存和磁盘缓存。内存缓存可以快速提供之前加载过的图片,而磁盘缓存则可以在应用重新启动后或网络不可用时提供图片。
Glide 会根据图片的大小和访问频率智能地管理缓存,以减少重复加载和下载的开销。
3 图片解码与缩放:
Glide 也是使用Android原生代码进行图片解码,以最大限度地减少内存占用和计算开销。它会根据设备的屏幕密度和 ImageView 的尺寸智能地调整图片的大小和质量,避免加载过大或过小的图片。
4 生命周期管理:
Glide 紧密集成 Android 生命周期,可以感知 Activity 和 Fragment 的生命周期,自动暂停和恢复图片加载。这确保了图片加载与应用的生命周期保持一致,避免了不必要的资源消耗。
5 网络优化:
Glide 可以与流行的网络库(如 OkHttp)集成,利用这些库提供的连接池、重试机制等功能,进一步提高网络请求的性能。
3.生命周期管理:(正片来袭 Glide版本4.15.1)
Glide 能够感知到 Context(如 Activity 或 Fragment)的生命周期。这意味着当 Activity 或 Fragment 被暂停或销毁时,Glide 会相应地暂停或取消图片加载,从而避免了不必要的资源消耗。
首先是Glide的简单使用:
Glide.with(context)
.load("http://example.com/image.jpg")
.into(imageView);
这里使用的设计模式 : 建造者设计模式
下面我们根据源码来分析下:
1、with()获取RequestManager
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
//with还支持传入 Activity、FragmentActivity、Fragment、android.app.Fragment
//但是官方不建议activity,建议传入FragmentActivity或者androidx.appcompat.app.AppCompatActivity)
2、检测context的合法性,还有就是获取Glide。
@NonNull
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();
}
3、获取单例Glide
//通过双重检测机制获得一个单例Glide
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
3.1 检测和初始化Glide
static void checkAndInitializeGlide(
@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
// In the thread running initGlide(), one or more classes may call Glide.get(context).
// Without this check, those calls could trigger infinite recursion.
if (isInitializing) {
throw new IllegalStateException(
"Glide has been called recursively, this is probably an internal library error!");
}
isInitializing = true;
try {
//初始化Glide
initializeGlide(context, generatedAppGlideModule);
} finally {
isInitializing = false;
}
}
3.2 初始化Glide
从下面代码,我们可以看到Glide从builder.build函数创建出来后,就放到一个全局静态变量上。
private static volatile Glide glide;
...
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
... //我们只看Glide创建的,其他先忽略
Glide glide = builder.build(applicationContext, manifestModules, annotationGeneratedModule);
...
Glide.glide = glide;
}
3.2.1 builder.build函数
@NonNull
Glide build(
@NonNull Context context,
List<GlideModule> manifestModules,
AppGlideModule annotationGeneratedGlideModule) {
...
...
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory, experiments);
return new Glide(
context, //应用程序的上下文
engine, //图片加载引擎,这是负责实际图片加载和缓存
memoryCache, //内存缓存
bitmapPool, //bitmap池,用于重用和回收不再需要的位图,以减少内存使用和分配次数
arrayPool, //数组池。用于管理内存中的数组,提高内存使用效率
requestManagerRetriever, //求管理器检索器。用于获取与特定上下文相关的 RequestManager 实例
connectivityMonitorFactory, //连接监控器工厂。用于监控网络连接状态,影响图片加载策略。
logLevel,//日志级别
defaultRequestOptionsFactory, //默认请求选项工厂。用于创建默认的 RequestOptions,这些选项可以应用于所有的图片加载请求
defaultTransitionOptions, //默认过渡选项。定义图片加载时的过渡动画
defaultRequestListeners, //默认请求监听器。允许你监听图片加载过程中的各种事件,如开始加载、加载成功、加载失败等
manifestModules, //清单模块。这是一个列表,包含所有需要在 Glide 初始化时注册的模块。
annotationGeneratedGlideModule, //注解生成的 Glide 模块。这通常是用于集成 Glide 和其他库或框架的自动生成的代码。
experiments);
//从传入的信息可以看出,创建一个 Glide 的实例配置了多种参数。然后我们就获取了一个Glide实例
}
4、Glide创建后,我们看到RequestManager(Appplication)
下面代码我们可以看到,我们如果调用的时候是主线程,则会使用我们传入的context,如果不是则会去获取ApplicationContext,这是我们使用的时候不用担心是否为主线程的原因。
@NonNull
public RequestManager get(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) { //检测是否为主线程和是否为Application的context
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper
// Only unwrap a ContextWrapper if the baseContext has a non-null application context.
// Context#createPackageContext may return a Context without an Application instance,
// in which case a ContextWrapper may be used to attach one.
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
4.1 RequestManager的创建
从下述代码不难看出,如果传入的是applicationContext则这个Glide会把资源保留,根据application的生命周期走,所以当我们使用的时候要注意不要传入applicationContext。
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or
// activity. However, in this case since the manager attached to the application will not
// receive lifecycle events, we must force the manager to start resumed using
// ApplicationLifecycle.
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(), //Application的生命周期
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
4.2 绑定生命周期
private final Runnable addSelfToLifecycle =
new Runnable() {
@Override
public void run() {
//生命周期注册
lifecycle.addListener(RequestManager.this);
}
};
...
RequestManager(
Glide glide,
Lifecycle lifecycle,
RequestManagerTreeNode treeNode,
RequestTracker requestTracker,
ConnectivityMonitorFactory factory,
Context context) {
this.glide = glide;
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.context = context;
connectivityMonitor =
factory.build(
context.getApplicationContext(),
new RequestManagerConnectivityListener(requestTracker));
// Order matters, this might be unregistered by teh listeners below, so we need to be sure to
// register first to prevent both assertions and memory leaks.
glide.registerRequestManager(this);
// If we're the application level request manager, we may be created on a background thread.
// In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
// issue by delaying adding ourselves as a lifecycle listener by posting to the main thread.
// This should be entirely safe.
if (Util.isOnBackgroundThread()) {
//抛回主线程
Util.postOnUiThread(addSelfToLifecycle);
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor);
defaultRequestListeners =
new CopyOnWriteArrayList<>(glide.getGlideContext().getDefaultRequestListeners());
setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());
}
4.3 生命周期绑定后
生命周期绑定后,则拿activtiy举例,
当Activiy生命周期到onstart时候,则开始发起请求获取图片。
当onstop的时候开始停止获取图片
onDestroy的时候取消请求
/**
* Lifecycle callback that registers for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and restarts failed or paused
* requests.
*/
@Override
public synchronized void onStart() {
resumeRequests();
targetTracker.onStart();
}
/**
* Lifecycle callback that unregisters for connectivity events (if the
* android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
*/
@Override
public synchronized void onStop() {
pauseRequests();
targetTracker.onStop();
}
/**
* Lifecycle callback that cancels all in progress requests and clears and recycles resources for
* all completed requests.
*/
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
Util.removeCallbacksOnUiThread(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
4、Glide创建后,我们看到RequestManager(activity、fragment、view)
当context是activity、fragment、view的时候,都会走到fragmentGet的函数
并且传入它的fragmentMnager
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else if (activity instanceof FragmentActivity) {
return get((FragmentActivity) activity);
} else {
assertNotDestroyed(activity);
frameWaiter.registerSelf(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /* parentHint= */ null, isActivityVisible(activity));
}
}
4.1将传入的fragmentManager传入
首先,通过传入的activity引用,获取当前页面的FragmentManager,然后将当前页面的引用和刚生成的FragmentManager对象引用,作为参数一起传入fragmentGet(activity, fm)方法。下面看下fragmentGet(activity, fm)的具体实现:
@NonNull
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
// 基于当前activity注册无UI的RequestManagerFragment
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// This is a bit of hack, we're going to start the RequestManager, but not the
// corresponding Lifecycle. It's safe to start the RequestManager, but starting the
// Lifecycle might trigger memory leaks. See b/154405040
if (isParentVisible) {
requestManager.onStart();
}
current.setRequestManager(requestManager);
}
return requestManager;
}
getRequestManagerFragment()做了什么?
生成了一个空白的fragment
@NonNull
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm, @Nullable android.app.Fragment parentHint) {
// If we have a pending Fragment, we need to continue to use the pending Fragment. Otherwise
// there's a race where an old Fragment could be added and retrieved here before our logic to
// add our pending Fragment notices. That can then result in both the pending Fragmeng and the
// old Fragment having requests running for them, which is impossible to safely unwind.
RequestManagerFragment current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
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;
}
RequestManagerFragment生成后会同时初始化lifeCycle
public RequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
@VisibleForTesting
@SuppressLint("ValidFragment")
RequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {
this.lifecycle = lifecycle;
}
4.2 生成lifecycle后,会在factory.build传入 current.getGlideLifecycle(),然后绑定流程和4.2开始一致
@NonNull
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
// 基于当前activity注册无UI的RequestManagerFragment
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// This is a bit of hack, we're going to start the RequestManager, but not the
// corresponding Lifecycle. It's safe to start the RequestManager, but starting the
// Lifecycle might trigger memory leaks. See b/154405040
if (isParentVisible) {
requestManager.onStart();
}
current.setRequestManager(requestManager);
}
return requestManager;
}
下面是上面用到的主要类的工作内容
RequestManagerRetriever:
RequestManagerRetriever是一个中间类,负责获取RequestManager实例。在大多数情况下,它会与Activity或Fragment的生命周期绑定,确保RequestManager的正确创建和销毁。当需要加载图片时,Glide库会通过RequestManagerRetriever获取RequestManager实例,以便管理图片加载请求。
RequestManagerFragment:
RequestManagerFragment是一个无UI的Fragment,它内部持有一个RequestManager实例。
当与Activity或Fragment绑定时,RequestManagerFragment能够感知其宿主的生命周期,并在适当的时候调用RequestManager的方法来暂停、恢复或清除请求。
RequestManager:
RequestManager是Glide库中用于管理图片加载请求的核心类。
它实现了LifeCycleListener接口,这意味着它可以响应宿主(通常是Activity或Fragment)的生命周期事件。RequestManager提供了如pauseRequests(), resumeRequests(), clearRequests()等方法,以根据宿主的生命周期状态管理图片加载请求。
LifeCycleListener:
LifeCycleListener是一个接口,它定义了生命周期管理方法,如onStart(), onStop(), 和onDestroy()。
任何实现了这个接口的类都需要提供这些方法的具体实现,以便在宿主的生命周期事件发生时得到通知。
ActivityFragmentLifecycle:
ActivityFragmentLifecycle是一个管理Fragment和RequestManager映射关系的类。
它可能还负责管理LifecycleListener的注册和注销,确保在Fragment的生命周期事件发生时,相应的RequestManager能够得到通知并作出相应的操作。空白Fragment(通常是一个不展示UI的Fragment)会回调这些方法,以便RequestManager可以适当地暂停和恢复图片加载请求。
强大的缓存策略:
Glide内置了强大的缓存机制,包括内存缓存和磁盘缓存。它可以根据设备的可用内存和存储空间智能地管理缓存,以确保图片的快速加载和节省流量。
Glide 主要分两大类 内存缓存 和 磁盘缓存
内存缓存 —— 防止应用重复将图片数据读取到内存当中
磁盘缓存 —— 防止应用重复从网络或其他地方重复下载和读取数据。
这两大缓存还有继续细分为这四类:
活动缓存 : 当前正在被应用使用的图片资源
LRU缓存 : 已经加载但当前不在屏幕上显示的图片的内存 (遵循最近最少使用算法)
资源缓存 : 已经解码并准备好用于显示的图片资源
原始缓存 : 原始的图片数据,即未经解码或修改的图片数据
Glide 的缓存策略 我们主要关注内存缓存和磁盘缓存这两大类就
我们知道缓存就是把东西存放到一个地方,然后等到我们用的时候就会从这个地方拿出来,那我们怎么去把我们之前存放的东西拿出来呢?
就好像我们去游泳前,会在储物柜放东西,等我们走的时候再从储物柜把东西拿出来。那我们怎么保证别人拿不走我们的东西,而且我们只能拿走我们存放的东西呢?这就涉及到KEY。
Glide缓存也不例外,他也会生成一个KEY
下面让我们通过源码来逐步分析
-> RequestBuilder.into
-> RequestManager.track
-> RequestTrack.runRequest
-> SingleRequest.begin
-> SingleRequest.onSizeReady
-> Engine.load
public <R> LoadStatus load(...) {
...
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
EngineResource<?> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
....
}
我们从into函数走到Engine.load函数,会看到Engin.load函数是生成我们缓存key的地方,会看到我们生成一个缓存key居然需要那么多参数,像宽高也是改变我们的key的生成。
缓存Key的生成条件我们看完了,那我们继续看内存缓存。
内存缓存
内存缓存也是从Engin.load函数获取看loadFromMenmory函数
@Nullable
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
//活动缓存
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return active;
}
//内存缓存
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return cached;
}
return null;
}
可以看到上述代码,假如我们loadFromActiveResources获取活动缓存失败,则从loadFromCache获取LRU缓存,那这个LRU缓存如何来的呢?让我们继续看下去。
Glie.with()
-> Glide.getRetriever
-> Glide.get
-> Glide.checkAndInitializeGlide
-> Glide.initializeGlide
-> Glide.initializeGlide
-> GlideBuilder.build
@NonNull
Glide build(@NonNull Context context) {
...
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
...
}
当Glide初始化时,就会生成LruBitmapPool,Lru就是最近少使用算法(当内存不足时,把最近最少使用的一块缓存淘汰)
LruBitmapPool 遵循LRU算法。
总结:内存缓存获取:会先从活动缓存中获取,如果获取失败,则会从LRU缓存池中获取缓存。
RequestManager : 一个用于管理和启动Glide请求的类。可以使用活动、片段和连接生命周期事件智能地停止、启动和重新启动请求。通过实例化一个新对象进行检索,或者为了利用内置的“活动”和“片段”生命周期处理,请在“片段”或“活动”中使用静态Glide.load方法。
磁盘缓存
Glide.with(context)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
把DiskCacheStrategy.NONE传入diskCacheStrategy方法,就可以把Glide的硬盘缓存功能禁用掉。
diskCacheStrategy可以接受这五个参数
DiskCacheStrategy.NONE:表示不缓存任何内容。
DiskCacheStrategy.DATA:表示只缓存原始图片。
DiskCacheStrategy.RESOURCE:表示只缓存转换过后的图片。
DiskCacheStrategy.ALL:表示既缓存原始图片,也缓存转换过后的图片。
DiskCacheStrategy.AUTOMATIC:表示让Glide根据图片资源智能地选择使用哪一种缓存策略(默认选项)
我们还是从Engin.load函数开始看
public <R> LoadStatus load(...) {
...
synchronized (this) {
//如果内存缓存没用获取到,就从磁盘缓存那里获取
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
//从磁盘缓存那里获取
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
// Avoid calling back while holding the engine lock, doing so makes it easier for callers to
// deadlock.
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
waitForExistingOrStartNewJob 主要做的事情是,会先从解析过的资源缓存获取,假如获取不到,就会从原始的资源磁盘缓存那里获取,假如都没有则会存放到磁盘缓存这里生成。
总结:Glide缓存获取顺序:内存缓存 ->磁盘缓存。
支持多种图片格式:
Glide支持的图片格式包括JPEG、PNG、GIF、WebP等常见图片格式。