当前位置:网站首页>一文搞懂Glide,不懂来打我
一文搞懂Glide,不懂来打我
2022-06-26 07:09:00 【Android每日一讲】
1、什么是Glide?
1.1、官方描述
Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。
- Glide 支持拉取,解码和展示视频快照、图片和GIF动画。Glide的Api灵活易用,开发者可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。
- Glide 的目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。
1.2、特点
- 自动、智能地采样(downsampling)和缓存(caching),最小化存储的开销和解码的次数;
- 有效的资源重用,例如字节数组和Bitmap,最小化垃圾回收和堆碎片的影响;
- 深度的生命周期集成,确保优先处理活跃的Fragment和Activity的请求,同时有利于应用在必要时释放资源(页面销毁等)。
2、Glide怎么使用?
2.1、官方 Glide API
Glide 使用简明的流式语法API,这是一个非常棒的设计,因为它允许你在大部分情况下一行代码搞定需求:
// build.gradle文件添加 Glide 依赖
dependencies {
implementation "com.github.bumptech.glide:glide:4.12.0"
}
// API 简单使用
Glide.with(context)
.load(url)
.into(imageView);
2.2、自定义 Glide API
通过Glide提供的注解,来添加自己定义的API
- GlideModule 注解用于AppGlideModule
- GlideExtension 注解用于标识一个扩展Glide API的类,任何拓展Glide API的类都必须使用这个注解来标记,被@GlideExtension注解的类应以工具类的方式实现。
- GlideOption 注解为RequestOptions添加一个选项。
- GlideType 添加新的资源类型的支持(GIF,SVG等)
项目需要通过GlideModule注解继承自AppGlideModule类的子类,并通过GlideExtension注解到工具类上,来扩展自定义Glide API,使用GlideOption、GlideType注解时必须为静态方法,最后经过Rebuild Project之后,最终会被编译到XXXRequest.java类
// build.gradle文件添加 Glide 注解处理器
dependencies {
implementation "com.github.bumptech.glide:glide:4.12.0"
annotationProcessor "com.github.bumptech.glide:compiler:4.12.0"
}
// Application模块内,GlideModule注解自定义子类继承AppGlideModule,可以不用重写任何方法。
@GlideModule(glideName = "GlideApp")
public class MyGlideModule extends AppGlideModule {
@Override
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
// 可以添加一些全局性的options
super.applyOptions(context, builder);
}
}
// GlideExtension注解,添加自定义的Glide API
@GlideExtension
public class MyGlideExtensions {
private MyGlideExtensions() {
}
// GlideOption注解,添加自定义的Option
@GlideOption
public static BaseRequestOptions<?> myMiniThumb(BaseRequestOptions<?> options, int size) {
return options.fitCenter().override(size);
}
// 自定义decode resource Type
private static final RequestOptions DECODE_TYPE_GIF = RequestOptions.decodeTypeOf(GifDrawable.class);
// GlideType注解,添加自定义的资源类型
@GlideType(GifDrawable.class)
public static RequestBuilder<GifDrawable> asMyGif(RequestBuilder<GifDrawable> requestBuilder) {
return requestBuilder
.transition(new DrawableTransitionOptions()) // 设置用于在加载完成时从占位符到正常显示的过渡效果
.apply(DECODE_TYPE_GIF); // 将自定义的ResourceClass设置到resourceClass参数
}
}
// 使用自定义API
GlideApp.with(context)
.asMyGif() // 使用自定义的资源
.load(url)
.myMiniThumb(100) // 使用自定义的Option
.into(view);
3、Glide加载图片过程
Glide加载图片的过程,可以分为三个阶段:with、load和into。
Glide 结构图如下:

3.1、with阶段
with方法用于获取到RequestManager,RequestManager用于管理图片请求;在创建RequestManager时,根据不同的Context上下文和线程,创建出绑定不同生命周期的组件(Application,Activity,Fragment)的requestManager实例。
RequestManager职责:
- 用于管理和启动 Glide 请求的类,通过内部的requestTracker来跟踪记录所有的request;
- 可以使用 Activity、Fragment 等连接生命周期事件来智能地停止、启动和重启请求;
通过不同的静态with方法,获取拥有不通生命周期的requestManager实例。
Glide#with(android.app.Activity)
Glide#with(androidx.fragment.app.FragmentActivity)
Glide#with(android.app.Fragment)
Glide#with(androidx.fragment.app.Fragment)
Glide#with(Context)
Glide#with(View)
// 对应到上述with方法,通过不同的get重载方法来创建或检索 requestManager 对象
RequestManagerRetriever#get(android.app.Activity)
RequestManagerRetriever#get(androidx.fragment.app.FragmentActivity)
RequestManagerRetriever#get(android.app.Fragment)
RequestManagerRetriever#get(androidx.fragment.app.Fragment)
RequestManagerRetriever#get(Context)
RequestManagerRetriever#get(View)
Glide with 流程图如下:

3.1.1、获取 Glide 单例
首先从Glide.with(Context)方法开始
# Glide.java
// 通过retriever 的get方法来获取requestManager
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
// 获取 retriever 检索器,其内部持有RequestManagerFactory,检索器用于创建或检索 requestManager 实例
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
return Glide.get(context).getRequestManagerRetriever();
}
// 获取Glide,Glide内部持有检索器
public static Glide get(@NonNull Context context) {
// 双重检查锁的方式,获取 glide 单例
if (glide == null) {
// 获取App模块内自定义的AppGlideModule类(*GlideModule注解的)
GeneratedAppGlideModule annotationGeneratedModule =
getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
// 创建 Glide 实例
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
// checkAndInitializeGlide方法最终会调用initializeGlide方法
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder, // builder = new GlideBuilder()
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
RequestManagerRetriever.RequestManagerFactory factory =
annotationGeneratedModule != null
// 获取到创建requestManager的factory,factory会创建自动生成的GlideRequests继承自RequestManager,其包含GlideType注解的API方法
? annotationGeneratedModule.getRequestManagerFactory()
: null;
builder.setRequestManagerFactory(factory);
if (annotationGeneratedModule != null) {
annotationGeneratedModule.applyOptions(applicationContext, builder);
}
// 构建 glide 实例,内部会创建默认的 RequestManagerRetriever和RequestManagerFactory等创建requestManager的相关参数。
Glide glide = builder.build(applicationContext);
if (annotationGeneratedModule != null) {
annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
}
applicationContext.registerComponentCallbacks(glide);
// 设置到静态变量,单例模式
Glide.glide = glide;
}
3.1.2、获取 RequestManager 实例
在Glide创建过程中会 创建检索器 RequestManagerRetriever,通过检索器获取 requestManager,接着分析 RequestManagerRetriever.get(Context)方法
# RequestManagerRetriever.java
public RequestManager get(@NonNull Context context) {
// 省略了安全检查
if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
// 获取 FragmentActivity 级 RequestManager
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
// 获取 Activity 级 RequestManager
return get((Activity) context);
} else if (context instanceof ContextWrapper
// 使用 ContextWrapper 附加的 Context 继续获取
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
// 获取 Application 级 RequestManager
return getApplicationManager(context);
}
// 跟踪 FragmentActivity 方式获取的 RequestManager
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
// 非主线程时,获取 Application 级 RequestManager
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity); // activity 销毁检查
frameWaiter.registerSelf(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
// 获取 Fragment 级 RequestManager
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
// 获取 SupportRequestManagerFragment,其内部持有:
// 1、ActivityFragmentLifecycle,在 Fragment 的模版方法中通过 lifecycle 回调生命周期事件
// 2、RequestManagerTreeNode,用于跟踪记录嵌套的 Fragment 的RequestManager
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// 获取全局 glide 单例
Glide glide = Glide.get(context);
// 通过工厂方式构建 requestManager 对象
requestManager =
factory.build(
glide,
// 1. requestManager 通过 lifecycle 注册listener
// 2. 回调生命周期事件,在Fragment生命周期变化时,通知RequestManager实现的LifecycleListener接口方法进行响应
current.getGlideLifecycle(),
current.getRequestManagerTreeNode(), // 跟踪嵌套Fragment内的RequestManager
context);
if (isParentVisible) {
// 开始请求,并设置target显示
requestManager.onStart();
}
// 给 fragment 设置RequestManger
current.setRequestManager(requestManager);
}
return requestManager;
}
3.1.3、Glide 生命周期分析
RequestMananger的构造方法中,和创建的用于监听生命周期事件的Fragment进行关联,RequestManager实现了LifeCycleListener接口,通过LifeCycle.addListener(this)的方式将观察者注入生命周期监视器。 RequestManager在实现了LifeCycleListener接口的onStart()/onStop()/onDestory()的方法中,通过RequestTracker来管理请求任务,通过TargetTracker来控制View的显示效果。
# RequestManager.java
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));
// 非主线程,切换到主线程绑定生命周期事件
if (Util.isOnBackgroundThread()) {
Util.postOnUiThread(addSelfToLifecycle);
} else {
// 监听生命周期事件
lifecycle.addListener(this);
}
// 监听网络变化事件
lifecycle.addListener(connectivityMonitor);
...
}
@Override
public synchronized void onStart() {
// 恢复请求
requestTracker.resumeRequests();
targetTracker.onStart();
}
@Override
public synchronized void onStop() {
// 暂停请求
requestTracker.pauseRequests();
targetTracker.onStop();
}
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
// 清理target
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
// 清除请求
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
Util.removeCallbacksOnUiThread(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
# RequestTracker.java
public void resumeRequests() {
isPaused = false;
// 处理所有的请求
for (Request request : Util.getSnapshot(requests)) {
if (!request.isComplete() && !request.isRunning()) {
// 重新发起请求
request.begin();
}
}
// pending队列保存未完成并排队等待再次运行的请求。 列表来维护对这些请求的硬引用,确保它们在开始运行之前或暂停时不会被垃圾收集,在重启开启请求时清理。
pendingRequests.clear();
}
3.1.4、Glide 网络变化分析
- 设置自定义的网络监听方式;
- 未设置自定义网络监听方式,采用默认方式;
- 有网络权限时,通过
ConnectivityManager.android.net.conn.CONNECTIVITY_CHANGE广播的方式监听网络事件。 - 无网络权限时,不监听。
# DefaultConnectivityMonitorFactory.java
// 默认监听网络变化广播
public ConnectivityMonitor build(
@NonNull Context context, @NonNull ConnectivityMonitor.ConnectivityListener listener) {
int permissionResult = ContextCompat.checkSelfPermission(context, NETWORK_PERMISSION);
// 检查网络权限
boolean hasPermission = permissionResult == PackageManager.PERMISSION_GRANTED;
return hasPermission
? new DefaultConnectivityMonitor(context, listener)
: new NullConnectivityMonitor();
}
# ReuquestManager.java
// 网络连接变化事件处理
private class RequestManagerConnectivityListener
implements ConnectivityMonitor.ConnectivityListener {
@GuardedBy("RequestManager.this")
private final RequestTracker requestTracker;
RequestManagerConnectivityListener(@NonNull RequestTracker requestTracker) {
this.requestTracker = requestTracker;
}
@Override
public void onConnectivityChanged(boolean isConnected) {
if (isConnected) {
synchronized (RequestManager.this) {
// 通过 requestTracker 重新发起请求
requestTracker.restartRequests();
}
}
}
}
3.2、load阶段
load阶段创建出 RequestBuilder 对象,为每个请求封装 glide,requestManager,glideContext,model,requestOptions 等参数。
RequestBuilder extends BaseRequestOptions {}
3.3、into阶段
into阶段可以分为四个过程:
- target 绑定 request 并发起 request 请求;
- 数据加载;
- 资源解码;
- 资源缓存和显示;
Glide into 流程图如下:

# RequestBuilder.java
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
// 省略了安全检查
// 省略了 options 变换
return into(
// 获取 ImageView 载体
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
// 主线程
Executors.mainThreadExecutor());
}
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
// 构建 Request 加载请求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
// 载体原有请求与新请求比对,请求等效时采用原有请求进行加载
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
// 当请求未开始时,开始启动请求,加载数据
previous.begin();
}
return target;
}
// 清理原来的请求
requestManager.clear(target);
// 将请求与 Target 进行绑定
target.setRequest(request);
// 记录请求,并启动请求
requestManager.track(target, request);
return target;
}
# RequestManager.java
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target);
// 记录请求,并启动请求
requestTracker.runRequest(request);
}
# RequestTracker.java
public void runRequest(@NonNull Request request) {
// 记录请求
requests.add(request);
if (!isPaused) {
// 启动请求
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
// 如果请求处于暂停状态,则记录加载请求,等状态恢复时,进行重新启动
pendingRequests.add(request);
}
}
# SingleRequest.java
public void begin() {
synchronized (requestLock) {
// 省略了安全检查
if (status == Status.COMPLETE) {
// 直接从请求中缓存的 Resource 返回,回调给 ViewTarget 显示资源
onResourceReady(
resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
// 从内存,本地或者远端加载数据
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
// 显示默认占位图
target.onLoadStarted(getPlaceholderDrawable());
}
}
}
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
// 开启加载,engine 是 Glide创建时构造的
loadStatus =
engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
}
}
3.3.1、数据加载流程
数据加载分为两个部分,一部分是内存(活跃资源 HashMap 和内存 LruCache)中加载;另一部分是从本地或远端加载。
- 缓存策略,决定缓存的数据类型:
| 缓存策略 | 是否支持转换资源缓存 | 是否支持原始数据缓存 |
|---|---|---|
| NONE | 不支持 | 不支持 |
| ALL | 数据源不是磁盘与内存缓存时,支持 | 数据源是远程,支持 |
| RESOURCE | 支持 | 不支持 |
| DATA | 不支持 | 数据源不是磁盘与内存缓存时,支持 |
| AUTOMIC | 数据源是本地,支持 | 数据源是远程,支持 |
- 缓存数据类型对应的加载器:
| 缓存类型 | Generator | 描述 |
|---|---|---|
| RESOURCE | ResourceCacheGenerator | 从包含采样/转换资源数据的缓存文件生成DataFetcher |
| DATA | DataCacheGenerator | 从包含原始未修改源数据的缓存文件生成DataFetcher |
| SOURCE | SourceGenerator | 使用注册的ModelLoaders和为加载提供的模型从原始源数据生成DataFetcher |
| FINISHED | NULL | NULL |
3.3.1.1、内存数据加载流程
内存中缓存的数据分为两种,一种是活跃资源的Map缓存,一种是LRU缓存,数据首先会从这两个缓存中加载,如果有则直接返回使用,如果资源为null,则从本地或远端数据加载数据。
内存数据加载流程图如下:

# Engine.java
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);
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);
}
}
// 将内存中加载的资源回调给 ViewTarget 显示
cb.onResourceReady(
memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
return null;
}
private EngineResource<?> loadFromMemory(
EngineKey key, boolean isMemoryCacheable, long startTime) {
if (!isMemoryCacheable) {
return null;
}
// 从获取资源缓存中加载
EngineResource<?> active = loadFromActiveResources(key);
if (active != null) {
return active;
}
// 从内存缓存中加载
EngineResource<?> cached = loadFromCache(key);
if (cached != null) {
return cached;
}
return null;
}
// 加载活跃资源
private EngineResource<?> loadFromActiveResources(Key key) {
EngineResource<?> active = activeResources.get(key);
if (active != null) {
active.acquire();
}
return active;
}
// 加载内存资源
private EngineResource<?> loadFromCache(Key key) {
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
// 缓存活跃资源,弱引用方式保存到 Map 中
activeResources.activate(key, cached);
}
return cached;
}
private EngineResource<?> getEngineResourceFromCache(Key key) {
// 从 LruCache 中加载
Resource<?> cached = cache.remove(key);
final EngineResource<?> result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
result = (EngineResource<?>) cached;
} else {
// 使用 EngineResource包装缓存资源
result =
new EngineResource<>(
cached, /*isMemoryCacheable=*/ true, /*isRecyclable=*/ true, key, /*listener=*/ this);
}
return result;
}
3.3.1.2、本地或远端数据加载流程
当从内存中没有找到资源时,会开启本地或远端数据加载的操作,此过程是异步行为,通过线程池方式提交加载任务启动加载请求。
本地或者远程数据加载流程图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8XFrEHyj-1655819995698)(https://upload-images.jianshu.io/upload_images/27287140-e29e021351d49390.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
# Engine.java
private <R> LoadStatus waitForExistingOrStartNewJob() {
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}
// 创建 EngineJob
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
// 创建 DecodeJob,解码流程
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob); // 开启异步加载流程,decodeJob 实现了 Runnable 接口
return new LoadStatus(cb, engineJob);
}
# DecodeJob.java
public void run() {
// 匹配 DataFetcherGenerator 进行数据加载
runWrapped();
}
// 从 runWrapped 开始,会调用到 runGenerators 方法
private void runGenerators() {
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
// 开始加载数据,ResourceCacheGenerator,DataCacheGenerator,SourceGenerator
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
# ResourceCacheGenerator.java,源码分析该DataFetcherGenerator
public boolean startNext() {
List<Key> sourceIds = helper.getCacheKeys();
while (modelLoaders == null || !hasNextModelLoader()) {
// Resource 是转码后的资源类型,对应的 Key 为 ResourceCacheKey
currentKey =
new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoops
helper.getArrayPool(),
sourceId,
helper.getSignature(),
helper.getWidth(),
helper.getHeight(),
transformation,
resourceClass,
helper.getOptions());
cacheFile = helper.getDiskCache().get(currentKey);
if (cacheFile != null) {
sourceKey = sourceId;
// 根据 CacheFile 匹配出所有符合的 ModelLoaders
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
// 通过 ModelLoader 构造出 LoadData
loadData =
modelLoader.buildLoadData(
cacheFile, helper.getWidth(), helper.getHeight(), helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
// 通过 DataFetcher 开始加载数据
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
# FileLoader.java 内对应的 FileFetcher 嵌套类
public void loadData(@NonNull Priority priority, @NonNull DataFetcher.DataCallback<? super Data> callback) {
try {
// file 为 modelLoader.buildLoadData 时传入的 model,即 cacheFile
// opener 是 FileInputStream
data = opener.open(file);
// 将打开的文件流数据,成功回调
callback.onDataReady(data);
} catch (FileNotFoundException e) {
// 失败回调
callback.onLoadFailed(e);
}
}
3.3.2、资源解码流程
在数据被加载成功之后,会进行资源的解码操作,转成Android可以支持显示的资源数据。
Glide 解码资源流程图如下:

# DecodeJob.java
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
// 省略了变量的赋值操作
if (Thread.currentThread() != currentThread) {
// 切换到指定线程进行资源解码操作
runReason = DecodeJob.RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
// 资源解码
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
// 通过 LoadPath 进行解码,loadPath 内有 decodePaths
return runLoadPath(data, dataSource, path);
}
# DecodePath.java
public Resource<Transcode> decode(
DataRewinder<DataType> rewinder,
int width,
int height,
@NonNull Options options,
DecodeCallback<ResourceType> callback)
throws GlideException {
// 解码资源,DecodePath内部会匹配注册的Decoder进行decode操作,解码出原始的Resource
Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
// 资源转码,DecodeJob是实现方,内部通过匹配注册的Transformation进行transform操作
// 最后根据缓存策略,决定缓存转码资源或者原始资源
Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
// 转换为新资源类型,eg: Bitmap -> BitmapDrawable
return transcoder.transcode(transformed, options);
}
3.3.3、资源缓存和显示流程
资源在经过加载和解码之后,进行转码阶段时,根据DataSource判断资源是缓存原始资源还是转码资源,策略如下:
| EncodeStrategy | 缓存到磁盘策略描述 | 缓存Key |
|---|---|---|
| SOURCE | 将原始资源数据缓存到磁盘 | DataCacheKey |
| TRANSFORMED | 将转码后的资源数据缓存到磁盘 | ResourceCacheKey |
| NONE | 数据不缓存到磁盘 |
资源缓存方案主要是表中三种:
| 缓存方案 | 方案介绍 |
|---|---|
| ActiveResource | 内存缓存,采用Map<Key, WeakReference> 弱引用的方案保存正在使用的资源,防止出现LRU导致正在使用的资源被回收 |
| LruCache | 内存缓存,采用最近最少使用的策略,保证资源的使用效率,且尽量避免出现OOM问题 |
| DiskLruCache | 磁盘缓存,最近最少使用的策略,减少对网络耗时的请求操作 |
在经过DataFetcher.loadData数据提取之后,进行数据的一个缓存,缓存分两种,一种是缓存到磁盘(默认是应用data目录下的image_manager_disk_cache文件,默认大小为250M),一种是缓存到内存。
Glide 资源缓存和显示流程图如下:

// 1、缓存活跃资源
# Engine.java
public synchronized void onEngineJobComplete(
EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
if (resource != null && resource.isMemoryCacheable()) {
// 活跃资源缓存
activeResources.activate(key, resource);
}
}
# ActiveResources.java,内部通过 Map<Key, ResourceWeakReference> 缓存
synchronized void activate(Key key, EngineResource<?> resource) {
// 通过弱引用的方式持有资源
ResourceWeakReference toPut =
new ResourceWeakReference(
key, resource, resourceReferenceQueue, isActiveResourceRetentionAllowed);
// 将弱引用资源放入Map
ResourceWeakReference removed = activeEngineResources.put(key, toPut);
if (removed != null) {
removed.reset();
}
}
// 2、内存缓存资源,通过 LRU 最近最少使用方案
# Engine.java
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
activeResources.deactivate(cacheKey);
if (resource.isMemoryCacheable()) {
// Cache 是 MemoryCache,内部持有 LruCache
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource, /*forceNextFrame=*/ false);
}
}
# LruResourceCache.java,继承自LruCache
public synchronized Y put(@NonNull T key, @Nullable Y item) {
final int itemSize = getSize(item);
// 超出数量上限时,直接返回,并清理当前需要缓存的资源
if (itemSize >= maxSize) {
onItemEvicted(key, item);
return null;
}
if (item != null) {
currentSize += itemSize;
}
// 将资源放入 LinkedHashMap,会通过 afterNodeAccess()方法将最近访问数据放在双链表的尾部
@Nullable Entry<Y> old = cache.put(key, item == null ? null : new Entry<>(item, itemSize));
if (old != null) {
currentSize -= old.size;
// 对废弃资源进行清理
if (!old.value.equals(item)) {
onItemEvicted(key, old.value);
}
}
// 重新计算缓存空间,大小超出时,则移除最久未使用的资源
evict();
return old != null ? old.value : null;
}
// 3、磁盘缓存资源,采用 DiskLruCache 方案
# DecodeJob.java 的嵌套类 DeferredEncodeManager
void encode(DiskCacheProvider diskCacheProvider, Options options) {
GlideTrace.beginSection("DecodeJob.encode");
try {
// 写入资源时,根据缓存策略,已经确定 toEncode 资源是转码资源还是原始资源
diskCacheProvider
.getDiskCache()
.put(key, new DataCacheWriter<>(encoder, toEncode, options));
} finally {
// 资源回收
toEncode.unlock();
GlideTrace.endSection();
}
}
// DiskLruCacheWrapper.java 内部持有 DiskLruCache
private synchronized DiskLruCache getDiskCache() throws IOException {
if (diskLruCache == null) {
// 打开磁盘缓存器
diskLruCache = DiskLruCache.open(directory, APP_VERSION, VALUE_COUNT, maxSize);
}
return diskLruCache;
}
public void put(Key key, Writer writer) {
// 将 Key 安全的转成 sha256 字符串编码,key会被保存在LruCache内,获取时会加锁
String safeKey = safeKeyGenerator.getSafeKey(key);
// 加锁,进行安全的写入操作
writeLocker.acquire(safeKey);
try {
try {
// 获取 DiskLruCache
DiskLruCache diskCache = getDiskCache();
// 如果资源已缓存,则退出
Value current = diskCache.get(safeKey);
if (current != null) {
return;
}
// 获取资源写入的编辑器
DiskLruCache.Editor editor = diskCache.edit(safeKey);
if (editor == null) {
throw new IllegalStateException("Had two simultaneous puts for: " + safeKey);
}
try {
// 写入到文件内
File file = editor.getFile(0);
if (writer.write(file)) {
editor.commit();
}
} finally {
editor.abortUnlessCommitted();
}
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Unable to put to disk cache", e);
}
}
} finally {
// 释放锁
writeLocker.release(safeKey);
}
}
# Extectors.java
// 主线程池,用于显示图片
private static final Executor MAIN_THREAD_EXECUTOR =
new Executor() {
@Override
public void execute(@NonNull Runnable command) {
Util.postOnUiThread(command);
}
}
4、总结
使用建议:
1. 结合自身需求,决定是否考虑在AppGlideModule内应用全局性的Options。
2. 在使用Glide时,尽可能的保证context上下文符合预期,防止产生内存泄漏问题。
3. 在滑动事件时,可以考虑结合RequestManager内的 resume 和 pause 来处理快速滑动产生的卡顿问题。
总结,Glide 框架主要分为三个部分:
- 第一个部分: with 阶段,注册编解码器,初始化变量(Glide,RequestManager,Engine等)和绑定页面生命周期等操作,用于管理请求和监听生命周期事件。
- 第二个部分:load 阶段,为每个请求配置单独的 Option,比如:设置 width,height,DiskCacheStrategy,Transaction等。
- 第三个部分:into 阶段,最复杂的阶段,启动请求,开始加载数据,对数据进行解码和转码操作,缓存解码数据或者原始数据,显示视图。
作者:jaymzyang
转载于:https://juejin.cn/post/7044079102839488543
如有侵权,请联系删除!
边栏推荐
- [feature extraction] feature selection of target recognition information based on sparse PCA with Matlab source code
- Porphyrin based polyimide (ppbpis); Synthesis of crosslinked porphyrin based polyimides (ppbpi CRS) porphyrin products supplied by Qiyue biology
- Iron / zinc / copper / platinum metal complexes such as 5,10,15,20-tetra (4-hydroxyphenyl) porphyrin (THPP) / (thppfe) / (thppzn) / (thppcu) / (thpppt) - Qiyue R & D
- MySQL (III)
- Massive log collection tool flume
- 【元胞自动机】基于元胞自动机实现高速公路收费站交通流问题附matlab代码
- Development trends and prospects of acrylamide crystallization market in the world and China 2022-2027
- 炒股怎么选择证券公司?手机开户安全么?
- The difference between insert ignore and insert into
- Promise API for getting started with the web
猜你喜欢

Paths with a certain value in a binary tree (1) (2) (3) (Sword finger offer)

Shell编程-用户信息管理

Matlab linear programming model learning notes

PyTorch搭建CNN-LSTM混合模型实现多变量多步长时间序列预测(负荷预测)

Dark red crystal meso-5,10,15,20-tetra (p-aminophenyl) cobalt porphyrin (co TAPP); Meso-5,10,15,20-tetra (p-aminophenyl) cobalt porphyrin no complex (TAPP co no) supplied by Qiyue

Zraqnhydae

Professional course - Code question record

Numpy learning challenge level 4 - numpy array attribute

MySQL operation database

unity之EasyAR使用
随机推荐
China's audio industry competition trend outlook and future development trend forecast report 2022 Edition
Promise API for getting started with the web
Crosslinked metalloporphyrin based polyimide ppbpi-h) PPBP Mn; PBP-Fe; PPBPI-Fe-CR; Ppbpi Mn CR product - supplied by Qiyue
【图像检测】基于Itti模型实现图像显著性检测附matlab代码
PXRD, IR, TGA of two-dimensional porphyrin COF (POR COF) /cof (2D pdpor COF) - supplied by Qiyue
网络io,磁盘io
5,10,15,20-tetra (4-methoxycarbonylphenyl) porphyrin tcmpp purple crystal; Meso-5,10,15,20-tetra (4-methoxyphenyl) porphyrin tmopp|zn[t (4-mop) p] and co[t (4-mop) p] complexes
Cocoscreator plays spine animation
ZRaQnHYDAe
item2安装配置及环境失效问题解决
The performance of iron and steel enterprises was expected to be good in January this year. Since February, the prices of products of iron and steel enterprises have increased significantly. A mighty
一芯实现喷雾|WS2812驱动|按键触摸|LED显示|语音播报芯片等功能,简化加湿器产品设计
【元胞自动机】基于元胞自动机实现高速公路收费站交通流问题附matlab代码
同花顺究竟如何开户,网上开户是否安全么?
The difference between insert ignore and insert into
Detailed materials of applying for residence permit in non local Beijing
炒股怎么选择证券公司?手机开户安全么?
How to open an account in flush? Is it safe to open an account online?
China polyphenylene oxide Market Development Prospect and Investment Strategy Research Report 2022-2027
Es string type (text vs keyword) selection