热点新闻
一文搞懂Glide,不懂来打我
2023-07-05 23:55  浏览:931  搜索引擎搜索“养老服务网”
温馨提示:信息一旦丢失不一定找得到,请务必收藏信息以备急用!本站所有信息均是注册会员发布如遇到侵权请联系文章中的联系方式或客服删除!
联系我时,请说明是在养老服务网看到的信息,谢谢。
展会发布 展会网站大全 报名观展合作 软文发布

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, 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 网络变化分析
  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 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阶段可以分为四个过程:

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

Glide into 流程图如下:






# RequestBuilder.java public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) { // 省略了安全检查 // 省略了 options 变换 return into( // 获取 ImageView 载体 glideContext.buildImageViewTarget(view, transcodeClass), 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, 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, 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, true, true, key, 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, 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
如有侵权,请联系删除!

发布人:3b51****    IP:117.173.23.***     举报/删稿
展会推荐
让朕来说2句
评论
收藏
点赞
转发