Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。
- Glide 支持拉取,解码和展示视频快照、图片和GIF动画。Glide的Api灵活易用,开发者可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。
- Glide 的目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。
Glide 使用简明的流式语法API,这是一个非常棒的设计,因为它允许你在大部分情况下一行代码搞定需求:
// build.gradle文件添加 Glide 依赖
dependencies {
implementation "com.github.bumptech.glide:glide:4.12.0"
}
// API 简单使用
Glide.with(context)
.load(url)
.into(imageView);
通过Glide提供的注解,来添加自己定义的API
项目需要通过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);
Glide加载图片的过程,可以分为三个阶段:with、load和into。
Glide 结构图如下:

with方法用于获取到RequestManager,RequestManager用于管理图片请求;在创建RequestManager时,根据不同的Context上下文和线程,创建出绑定不同生命周期的组件(Application,Activity,Fragment)的requestManager实例。
RequestManager职责:
通过不同的静态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 流程图如下:

首先从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;
}
在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;
}
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();
}
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();
}
}
}
}
load阶段创建出 RequestBuilder 对象,为每个请求封装 glide,requestManager,glideContext,model,requestOptions 等参数。
RequestBuilder extends BaseRequestOptions {}
into阶段可以分为四个过程:
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);
}
}
数据加载分为两个部分,一部分是内存(活跃资源 HashMap 和内存 LruCache)中加载;另一部分是从本地或远端加载。
| 缓存策略 | 是否支持转换资源缓存 | 是否支持原始数据缓存 |
|---|---|---|
| NONE | 不支持 | 不支持 |
| ALL | 数据源不是磁盘与内存缓存时,支持 | 数据源是远程,支持 |
| RESOURCE | 支持 | 不支持 |
| DATA | 不支持 | 数据源不是磁盘与内存缓存时,支持 |
| AUTOMIC | 数据源是本地,支持 | 数据源是远程,支持 |
| 缓存类型 | Generator | 描述 |
|---|---|---|
| RESOURCE | ResourceCacheGenerator | 从包含采样/转换资源数据的缓存文件生成DataFetcher |
| DATA | DataCacheGenerator | 从包含原始未修改源数据的缓存文件生成DataFetcher |
| SOURCE | SourceGenerator | 使用注册的ModelLoaders和为加载提供的模型从原始源数据生成DataFetcher |
| FINISHED | NULL | NULL |
内存中缓存的数据分为两种,一种是活跃资源的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;
}
当从内存中没有找到资源时,会开启本地或远端数据加载的操作,此过程是异步行为,通过线程池方式提交加载任务启动加载请求。
本地或者远程数据加载流程图如下:

# 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);
}
}
在数据被加载成功之后,会进行资源的解码操作,转成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);
}
资源在经过加载和解码之后,进行转码阶段时,根据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);
}
}
使用建议:
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
如有侵权,请联系删除!
在railscasts项目中你可以看到这段代码:before(:each)dologin_asFactory(:user,:admin=>true)end函数对应的定义是:Factory.define:userdo|f|f.sequence(:github_username){|n|"foo#{n}"}end我不明白admin参数是如何传递给函数的,而函数中没有关于admin参数的字样。谢谢 最佳答案 Factory.define不是一个函数定义,它是一个方法,它接受一个符号或字符串(在本例中是用户)和一个定义你正在制作的工厂的bl
一文解决关于VLAN所有的疑惑VLAN基本概念为什么需要VLAN?怎么在交换机上划分VLAN,VLAN的工作原理有了子网,已经隔离了广播,还需要VLAN干啥?只进行子网划分,不进行VLAN划分VLAN划分与子网划分附加VLAN信息的方法VLAN划分交换机的端口类型(Access和Trunk)一、访问链接二、汇聚链接汇聚链接VLAN间通信为什么要进行VLAN间通信?路由器实现VLAN间通信路由器和交换机的连接方式通信细节三层交换机实现VLAN间通信加速VLAN间通信三层交换机与路由器三层交换机路由器路由器和交换机配合构建LAN的实例使用VLAN设计局域网的特点VLAN增加网络的灵活性不使用VLA
在Ruby中,我看到这样一个方法的定义:def[](param)#dostuffend这个方法声明是什么意思?它是如何工作的?什么时候使用它?以及如何使用实例对象调用此类方法? 最佳答案 这是方法的名称,[]。您可能已经知道Array#[]或Hash#[]。在您的类(class)中,您也可以定义这样的方法。它会做什么-由您决定。classFoodef[](param)#bodyendendf=Foo.newf[:some_value] 关于ruby-一个方法定义以括号开头,看不懂它的用处
✅作者简介:大家好,我是小杨📃个人主页:「小杨」的csdn博客🔥系列专栏:小杨带你玩转C语言【初阶】🐳希望大家多多支持🥰一起进步呀!大家好呀!我是小杨。小杨花几天的时间将C语言中的操作符这部分知识做了一个大总结,在方便自己复习的同时也能够帮助到大家。通篇字数在一万字左右,可以算作是非常详细了,一文就可以带领大家彻底掌握操作符这部分内容,文章很长建议先收藏再看,防止下次想看就找不到啦。文章目录✍1,算术操作符✍2,移位操作符 🔍2.1,左移操作符 🔍2.2,右移操作符 ✨2.2.1,算术移位 ✨2.2.2,逻辑移位✍3,位操作符 🔍3.1,按位与&
每个企业都希望在完成项目后获得盈利,但不少企业到了年终后才发现项目做了不少,公司却并没能达到预期,甚至还出现了亏损。那么钱究竟去了哪里?很多公司都搞不清楚原因,出现糊涂账较多的状况,这将会造成严重的后果,尤其在疫情影响下,大环境很恶劣,如果是大公司的事业部门出现亏损,就可能会导致事业部门解散;如果是小公司出现亏损,就很容易导致公司倒闭;怎样做才能确保我们所完成的项目都能获利?从财务角度看,要确保盈利必须做到合理估算成本,只有这样才能在对外签订合约时做出合理报价,在对内在开始项目前做出充分评估投入代价,同时在实施过程中还要控制成本得当,最后项目结束时才会有可能获得盈利。那么我们怎样才能准确的判断
COINDAO旨在重建社区信任和安全。基于皖北基因的强烈共识,COINDAO自发产生了一个共创、共建、共治、共享的协作组织。它专注于DAO投资管理协议,为新的优质项目创造增长技术和资金。COINDAO的使命就是为真正的优质项目打造一个去中心化、公开透明的平台,让各个赛道上的优质项目能够以更低的成本快速募集资金并向公众开放.打破头部垄断。让真正的爱好者直接获得早期参与优质项目的资格,不再遥不可及,构建生态应用的可信体系,打造人人参与共建、人人共享的去中心化DAO好处。生态系统,我们称之为COINDAO生态系统。COINDAO国内各大财经网站宣发如下:COINDAO国外各大财经网站宣发: COIN
今天来说说前端低代码有多幸福?低代码是啥?顾名思义少写代码……这种情况下带来的幸福有:代码写得少,bug也就越少(所谓“少做少错”),因此开发环节的两大支柱性工作“赶需求”和“修bug”就都少了;要测的代码少了,那么测试用例也可以少写了。所以,总结低代码带来的幸福感有这三大点:开发效率提高开发成本减少维护性更高针对上述三点,我们展开说说。01、开发效率提高对于低代码的理解,个人认为可以通过配置化的低成本交互方式(主流是拖拽)加上少量的胶水代码,去满足一类应用的需求。这就说明,基于低代码,开发人员无需代码或说只需少量代码就可以开发出各类应用管理系统,如:OA协同办公、KM知识管理、CRM客户关系
我正在尝试检查目录是否作为node.js中命令行应用程序的一部分存在。但是,fs似乎并不理解~/。例如,以下返回false...>fs.existsSync('~/Documents')false...但这会返回true...>fs.existsSync('/Users/gtmtg/Documents')true...即使它们是同一回事。为什么会发生这种情况,是否有解决方法?提前致谢! 最佳答案 那是因为命令shell支持~/,而不是文件系统API。 关于javascript-Node.
目录🍊前言🍊:🍈一、宏与函数🍈: 1.宏与函数对比: 2.宏与函数的命名约定:🍓二、预处理操作符🍓: 1.预处理操作符"#": 2.预处理操作符"##":🥝三、条件编译🥝: 1.简述条件编译指令: 2.常见条件编译指令:🍒总结🍒:🛰️博客主页:✈️銮同学的干货分享基地🛰️欢迎关注:👍点赞🙌收藏✍️留言🛰️系列专栏:💐【进阶】C语言学习 🧧 C语言学习🛰️代码仓库:🎉VS2022_C语言仓库 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!
作者:翟天保Steven版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处一、设计模式是什么? 设计模式是为了解决在软件开发过程中遇到的某些问题而形成的思想。同一场景有多种设计模式可以应用,不同的模式有各自的优缺点,开发者可以基于自身需求选择合适的设计模式,去解决相应的工程难题。 良好的软件设计和架构,可以让代码具备良好的可读性、可维护性、可扩展性、可复用性,让整个系统具备较强的鲁棒性和性能,减少屎山代码出现的概率。 想要熟练运用设计模式,提高自己的编程能力和架构能力,只有在自己工作中,结合自身工作内容,多思考多实践。本文只能通过举一些通俗的例子,来