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、本地或远端数据加载流程
当从内存中没有找到资源时,会开启本地或远端数据加载的操作,此过程是异步行为,通过线程池方式提交加载任务启动加载请求。
本地或者远程数据加载流程图如下:
# 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
如有侵权,请联系删除!