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 {@Overridepublic void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {// 可以添加一些全局性的optionssuper.applyOptions(context, builder);}
}// GlideExtension注解,添加自定义的Glide API
@GlideExtension
public class MyGlideExtensions {private MyGlideExtensions() {}// GlideOption注解,添加自定义的Option@GlideOptionpublic static BaseRequestOptions<?> myMiniThumb(BaseRequestOptions<?> options, int size) {return options.fitCenter().override(size);}// 自定义decode resource Typeprivate 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参数}
}// 使用自定义APIGlideApp.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方法来获取requestManagerpublic 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.javapublic RequestManager get(@NonNull Context context) {// 省略了安全检查if (Util.isOnMainThread() && !(context instanceof Application)) {if (context instanceof FragmentActivity) {// 获取 FragmentActivity 级 RequestManagerreturn get((FragmentActivity) context);} else if (context instanceof Activity) {// 获取 Activity 级 RequestManagerreturn get((Activity) context);} else if (context instanceof ContextWrapper// 使用 ContextWrapper 附加的 Context 继续获取&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {return get(((ContextWrapper) context).getBaseContext());}}// 获取 Application 级 RequestManagerreturn getApplicationManager(context);}// 跟踪 FragmentActivity 方式获取的 RequestManagerpublic RequestManager get(@NonNull FragmentActivity activity) {if (Util.isOnBackgroundThread()) {// 非主线程时,获取 Application 级 RequestManagerreturn get(activity.getApplicationContext());} else {assertNotDestroyed(activity);   // activity 销毁检查frameWaiter.registerSelf(activity);FragmentManager fm = activity.getSupportFragmentManager();return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}}// 获取 Fragment 级 RequestManagerprivate RequestManager supportFragmentGet(@NonNull Context context,@NonNull FragmentManager fm,@Nullable Fragment parentHint,boolean isParentVisible) {// 获取 SupportRequestManagerFragment,其内部持有:// 1、ActivityFragmentLifecycle,在 Fragment 的模版方法中通过 lifecycle 回调生命周期事件// 2、RequestManagerTreeNode,用于跟踪记录嵌套的 Fragment 的RequestManagerSupportRequestManagerFragment 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内的RequestManagercontext);if (isParentVisible) {// 开始请求,并设置target显示requestManager.onStart();}// 给 fragment 设置RequestMangercurrent.setRequestManager(requestManager);}return requestManager;}
3.1.3、Glide 生命周期分析

RequestMananger的构造方法中,和创建的用于监听生命周期事件的Fragment进行关联,RequestManager实现了LifeCycleListener接口,通过LifeCycle.addListener(this)的方式将观察者注入生命周期监视器。 RequestManager在实现了LifeCycleListener接口的onStart()/onStop()/onDestory()的方法中,通过RequestTracker来管理请求任务,通过TargetTracker来控制View的显示效果。

# RequestManager.javaRequestManager(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);...}@Overridepublic synchronized void onStart() {// 恢复请求requestTracker.resumeRequests();targetTracker.onStart();}@Overridepublic synchronized void onStop() {// 暂停请求requestTracker.pauseRequests();targetTracker.onStop();}@Overridepublic synchronized void onDestroy() {targetTracker.onDestroy();// 清理targetfor (Target<?> target : targetTracker.getAll()) {clear(target);}targetTracker.clear();// 清除请求requestTracker.clearRequests();lifecycle.removeListener(this);lifecycle.removeListener(connectivityMonitor);Util.removeCallbacksOnUiThread(addSelfToLifecycle);glide.unregisterRequestManager(this);}# RequestTracker.javapublic void resumeRequests() {isPaused = false;// 处理所有的请求for (Request request : Util.getSnapshot(requests)) {if (!request.isComplete() && !request.isRunning()) {// 重新发起请求request.begin();}}// pending队列保存未完成并排队等待再次运行的请求。 列表来维护对这些请求的硬引用,确保它们在开始运行之前或暂停时不会被垃圾收集,在重启开启请求时清理。pendingRequests.clear();}
3.1.4、Glide 网络变化分析
  1. 设置自定义的网络监听方式;
  2. 未设置自定义网络监听方式,采用默认方式;
  • 有网络权限时,通过 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 RequestManagerConnectivityListenerimplements ConnectivityMonitor.ConnectivityListener {@GuardedBy("RequestManager.this")private final RequestTracker requestTracker;RequestManagerConnectivityListener(@NonNull RequestTracker requestTracker) {this.requestTracker = requestTracker;}@Overridepublic 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阶段可以分为四个过程:

  1. target 绑定 request 并发起 request 请求;
  2. 数据加载;
  3. 资源解码;
  4. 资源缓存和显示;

Glide into 流程图如下:

# RequestBuilder.javapublic 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.javasynchronized void track(@NonNull Target<?> target, @NonNull Request request) {targetTracker.track(target);// 记录请求,并启动请求requestTracker.runRequest(request);}# RequestTracker.javapublic 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.javapublic 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.javapublic <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.javaprivate <R> LoadStatus waitForExistingOrStartNewJob() {EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);if (current != null) {current.addCallback(cb, callbackExecutor);return new LoadStatus(cb, current);}// 创建 EngineJobEngineJob<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.javapublic 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,源码分析该DataFetcherGeneratorpublic boolean startNext() {List<Key> sourceIds = helper.getCacheKeys();while (modelLoaders == null || !hasNextModelLoader()) {// Resource 是转码后的资源类型,对应的 Key 为 ResourceCacheKeycurrentKey =new ResourceCacheKey( // NOPMD AvoidInstantiatingObjectsInLoopshelper.getArrayPool(),sourceId,helper.getSignature(),helper.getWidth(),helper.getHeight(),transformation,resourceClass,helper.getOptions());cacheFile = helper.getDiskCache().get(currentKey);if (cacheFile != null) {sourceKey = sourceId;// 根据 CacheFile 匹配出所有符合的 ModelLoadersmodelLoaders = helper.getModelLoaders(cacheFile);modelLoaderIndex = 0;}}loadData = null;boolean started = false;while (!started && hasNextModelLoader()) {ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);// 通过 ModelLoader 构造出 LoadDataloadData =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 是 FileInputStreamdata = opener.open(file);// 将打开的文件流数据,成功回调callback.onDataReady(data);} catch (FileNotFoundException e) {// 失败回调callback.onLoadFailed(e);}}
3.3.2、资源解码流程

在数据被加载成功之后,会进行资源的解码操作,转成Android可以支持显示的资源数据。

Glide 解码资源流程图如下:

# DecodeJob.javapublic 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 内有 decodePathsreturn runLoadPath(data, dataSource, path);}# DecodePath.javapublic Resource<Transcode> decode(DataRewinder<DataType> rewinder,int width,int height,@NonNull Options options,DecodeCallback<ResourceType> callback)throws GlideException {// 解码资源,DecodePath内部会匹配注册的Decoder进行decode操作,解码出原始的ResourceResource<ResourceType> decoded = decodeResource(rewinder, width, height, options);// 资源转码,DecodeJob是实现方,内部通过匹配注册的Transformation进行transform操作// 最后根据缓存策略,决定缓存转码资源或者原始资源Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);// 转换为新资源类型,eg: Bitmap -> BitmapDrawablereturn 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.javapublic 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);// 将弱引用资源放入MapResourceWeakReference removed = activeEngineResources.put(key, toPut);if (removed != null) {removed.reset();}}// 2、内存缓存资源,通过 LRU 最近最少使用方案# Engine.javapublic void onResourceReleased(Key cacheKey, EngineResource<?> resource) {activeResources.deactivate(cacheKey);if (resource.isMemoryCacheable()) {// Cache 是 MemoryCache,内部持有 LruCachecache.put(cacheKey, resource);} else {resourceRecycler.recycle(resource, /*forceNextFrame=*/ false);}}# LruResourceCache.java,继承自LruCachepublic 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 的嵌套类 DeferredEncodeManagervoid 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 内部持有 DiskLruCacheprivate 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 {// 获取 DiskLruCacheDiskLruCache 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() {@Overridepublic 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
如有侵权,请联系删除!

一文搞懂Glide,不懂来打我相关推荐

  1. 一文搞懂AWS EC2, IGW, RT, NAT, SG 基础篇下

    B站实操视频更新 跟着拉面学习AWS--EC2, IGW, RT, NAT, SG 简介 长文多图预警,看结论可以直接拖到"总结"部分 本文承接上一篇文章介绍以下 AWS 基础概念 ...

  2. 都2021年了,再不学ES6你就out了 —— 一文搞懂ES6

    JS干货分享 -- 一文搞懂ES6 导语:ES6是什么?用来做什么? 1. let 与 const 2. 解构赋值 3. 模板字符串 4. ES6 函数(升级后更爽) 5. Class类 6. Map ...

  3. 一文搞懂如何使用ArcGIS API for Python训练深度学习模型

    一文搞懂如何使用ArcGIS API for Python训练深度学习模型 文章目录 一文搞懂如何使用ArcGIS API for Python训练深度学习模型 写在前面 一.ArcGIS API f ...

  4. 一文搞懂RNN(循环神经网络)

    基础篇|一文搞懂RNN(循环神经网络) https://mp.weixin.qq.com/s/va1gmavl2ZESgnM7biORQg 神经网络基础 神经网络可以当做是能够拟合任意函数的黑盒子,只 ...

  5. 一文搞懂 Python 的 import 机制

    一.前言 希望能够让读者一文搞懂 Python 的 import 机制 1.什么是 import 机制? 通常来讲,在一段 Python 代码中去执行引用另一个模块中的代码,就需要使用 Python ...

  6. python语言语句快的标记是什么_一文搞懂Python程序语句

    原标题:一文搞懂Python程序语句 程序流 Python 程序中常用的基本数据类型,包括: 内置的数值数据类型 Tuple 容器类型 String 容器类型 List 容器类型 自然的顺序是从页面或 ...

  7. 一文搞懂 Java 线程中断

    转载自   一文搞懂 Java 线程中断 在之前的一文<如何"优雅"地终止一个线程>中详细说明了 stop 终止线程的坏处及如何优雅地终止线程,那么还有别的可以终止线程 ...

  8. 一文搞懂HMM(隐马尔可夫模型)-Viterbi algorithm

    ***一文搞懂HMM(隐马尔可夫模型)*** 简单来说,熵是表示物质系统状态的一种度量,用它老表征系统的无序程度.熵越大,系统越无序,意味着系统结构和运动的不确定和无规则:反之,,熵越小,系统越有序, ...

  9. 一文搞懂如何使用Node.js进行TCP网络通信

    摘要: 网络是通信互联的基础,Node.js提供了net.http.dgram等模块,分别用来实现TCP.HTTP.UDP的通信,本文主要对使用Node.js的TCP通信部份进行实践记录. 本文分享自 ...

最新文章

  1. 使用charles对vue项目进行map Local功能mock数据页面不正常显示
  2. 现代谱估计:多窗口谱相关性
  3. c语言中有bool型变量吗?
  4. SpringMVC第一个例子
  5. 1.5 编程基础之循环控制 20 球弹跳高度的计算
  6. 一位面试了阿里,滴滴,网易,蚂蚁金服,最终有幸去了网易的Java程序员【面试题分享】...
  7. auc 和loss_精确率、召回率、F1 值、ROC、AUC 各自的优缺点是什么?
  8. 2432功率计使用说明_Quarq功率计的安装、使用和维护
  9. redis技术分享ppt_技术分享丨华为鲲鹏架构Redis知识二三事
  10. 《代码整洁之道:程序员的职业素养》一一1.1 清楚你要什么
  11. OpenWrt 防火墙组件
  12. Installshield2008教程
  13. 实对称矩阵可对角化证明
  14. STAR、6W1H、SMART、PDCA、MKASH原则在精准招聘中的应用
  15. 多元线性回归及虚拟变量(哑变量)设置
  16. LiveData去除粘性
  17. java移位加密_凯撒加密解密(java字母移位)
  18. unity 粒子系统面板参数释义
  19. OSINT 和 15 大开源情报工具
  20. Zabbix的item数据采集、tigger监控规则、action触发动作原理介绍

热门文章

  1. IPv6都有哪些地址
  2. 星之匙,石之阶,火之行:我们能从EMUI 10.1读出什么
  3. PHP 面试踩过的坑
  4. 科技引领未来,银联开放平台诚邀你的加入
  5. 小游戏 (最少线段连线问题)
  6. 仿穷游项目--bug集
  7. 【邢不行|量化小讲堂系列58-实战篇】零基础了解什么是期权,抓住下轮牛市红利
  8. 获得当前窗口的hwnd的方法整理
  9. 临床病理的计算机辅助读片,第二军医大学附属长海医院病理科
  10. 电信手机信号测试软件,移动、联通、电信手机信号辐射实测~有图有真相