Glide是Android端开源图片加载库,能够帮助我们下载、缓存、展示多种格式图片。也是现在主流图片加载框架之一。源码内部究竟是如何实现的呢?讲解主流程,简略分析。

用法如下:

 Glide.with(context).load(url).into(imageView);

我这里拆分为三步分析:

一、with(context)

点击源码查看到是多个重载方法activity、fragment、view等等,下面用其中一个方法来展示

  @NonNullpublic static RequestManager with(@NonNull Activity activity) {return getRetriever(activity).get(activity);}
  @NonNullprivate static RequestManagerRetriever getRetriever(@Nullable Context context) {// Context could be null for other reasons (ie the user passes in null), but in practice it will// only occur due to errors with the Fragment lifecycle.Preconditions.checkNotNull(context,"You cannot start a load on a not yet attached View or a Fragment where getActivity() "+ "returns null (which usually occurs when getActivity() is called before the Fragment "+ "is attached or after the Fragment is destroyed).");return Glide.get(context).getRequestManagerRetriever();}

调用getRetriever方法获取RequestManagerRetriever对象。在创建该对象之前首先通过Glide.java中的get方法获得了Glide单例对象以及AppClideModule等配置。

@NonNullpublic static Glide get(@NonNull Context context) {if (glide == null) {GeneratedAppGlideModule annotationGeneratedModule =getAnnotationGeneratedGlideModules(context.getApplicationContext());synchronized (Glide.class) {if (glide == null) {checkAndInitializeGlide(context, annotationGeneratedModule);}}}return glide;}

下面的get方法可知道,在子线程不会添加生命周期;主线程添加一个空白的fragment来处理生命周期。最后返回RequestManager对象

  @NonNullpublic RequestManager get(@NonNull Context context) {if (context == null) {throw new IllegalArgumentException("You cannot start a load on a null Context");} else if (Util.isOnMainThread() && !(context instanceof Application)) {if (context instanceof FragmentActivity) {return get((FragmentActivity) context);} else if (context instanceof Activity) {return get((Activity) context);} else if (context instanceof ContextWrapper// Only unwrap a ContextWrapper if the baseContext has a non-null application context.// Context#createPackageContext may return a Context without an Application instance,// in which case a ContextWrapper may be used to attach one.&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {return get(((ContextWrapper) context).getBaseContext());}}return getApplicationManager(context);}//调用get判断线程@NonNullpublic RequestManager get(@NonNull FragmentActivity activity) {if (Util.isOnBackgroundThread()) {//子线程return get(activity.getApplicationContext());} else {//主线程添加生命周期assertNotDestroyed(activity);FragmentManager fm = activity.getSupportFragmentManager();return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));}}

二、load(url)

上面执行完成到这里已经拿到RequestManager对象,然后调用load(url)。看源码可知是多个重载方法,传不同类型的资源。最终拿到RequestBuilder对象

// RequestManager.java 的代码如下public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {return asDrawable().load(bitmap);}public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {return asDrawable().load(drawable);}public RequestBuilder<Drawable> load(@Nullable String string) {return asDrawable().load(string);}public RequestBuilder<Drawable> load(@Nullable Uri uri) {return asDrawable().load(uri);}public RequestBuilder<Drawable> load(@Nullable File file) {return asDrawable().load(file);}public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {return asDrawable().load(resourceId);}public RequestBuilder<Drawable> load(@Nullable URL url) {return asDrawable().load(url);}public RequestBuilder<Drawable> load(@Nullable byte[] model) {return asDrawable().load(model);}public RequestBuilder<Drawable> load(@Nullable Object model) {return asDrawable().load(model);}

三、into(imageView)

上一步拿到了RequestBuilder对象,调用into可知有2个重载方法。into的参数就是最终显示的控件。

into方法内部代码分支很多,代码庞大,所以只需走主流程如何显示ImageView的实现即可。当into内部代码执行完成后回到 buildImageViewTarget方法,这个方法是显示使用的,通过Executors.mainThreadExecutor())来切主线程,最终显示控件。

    return into(glideContext.buildImageViewTarget(view, transcodeClass),/*targetListener=*/ null,requestOptions,Executors.mainThreadExecutor());

点击到into内部源码如下:

  private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,@Nullable RequestListener<TranscodeType> targetListener,BaseRequestOptions<?> options,Executor callbackExecutor) {Preconditions.checkNotNull(target);if (!isModelSet) {throw new IllegalArgumentException("You must call #load() before calling #into()");}Request request = buildRequest(target, targetListener, options, callbackExecutor);Request previous = target.getRequest();if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {// If the request is completed, beginning again will ensure the result is re-delivered,// triggering RequestListeners and Targets. If the request is failed, beginning again will// restart the request, giving it another chance to complete. If the request is already// running, we can let it continue running without interruption.if (!Preconditions.checkNotNull(previous).isRunning()) {// Use the previous request rather than the new one to allow for optimizations like skipping// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions// that are done in the individual Request.previous.begin();}return target;}

这里处理请求

Request request = buildRequest(target, targetListener, options, callbackExecutor);

Request previous = target.getRequest();

将请求对象装到集合中,并且有加锁处理,运用于多线程的并发请求。

url请求走如下:

网络请求完成callback.onDataReady(result),开始一步一步往回传数据。在这一系列过程中,进行了数据处理,比如:图片压缩等。 省略N步骤

//  HttpUrlFetcher.java 代码如下@Overridepublic void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {long startTime = LogTime.getLogTime();try {InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());callback.onDataReady(result);} catch (IOException e) {if (Log.isLoggable(TAG, Log.DEBUG)) {Log.d(TAG, "Failed to load data for url", e);}callback.onLoadFailed(e);} finally {if (Log.isLoggable(TAG, Log.VERBOSE)) {Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));}}}

最后回到了ImageViewTarget类,显示控件。这就是整体简略主流程。

 @Overridepublic void setDrawable(Drawable drawable) {view.setImageDrawable(drawable);}

四、缓存原理分析

当加载图片会走2种方式:

1、是Http/IO  ;

2、三级缓存策略

一级缓存:活动缓存 ,当前Activity退出缓存销毁。

二级缓存:LRU内存缓存 ,APP应用退出缓存销毁。

三级缓存:LRU磁盘缓存 ,一直存在。

一、缓存机制加载流程:

获取顺序是,先从活动缓存取,如果没有就再去内存缓存取,如果还没是没有就再去磁盘缓存取,都没有就再去网络下载。

二、缓存介绍:

(1)  活动缓存:Glide自己实现的一种缓存策略,将使用的对象存放在HashMap,里面使用的弱引用,不需要时立即移除及时释放资源。

(2)内存缓存:使用的LRU算法进行处理,核心是使用 LinkedHashMap 实现,保存到内存中。

(3)磁盘缓存:使用的LRU算法进行处理,核心是使用 LinkedHashMap 实现,保存到磁盘中。(Glide使用DiskLruCache实现,将图片进行的加密、压缩处理,所以文件读写比普通IO处理效率高)

LRU的原理:假设 maxSize =3,当第4个数据进入时,移除最先未使用的。画图理解一哈:

LruCache类实际上是对LinkedHashMap进行的封装。上代码证明:

值得注意的是,第三个参数true代表访问排序

this.map = new LinkedHashMap<K, V>(0, 0.75f, true);

三、活动缓存的意义

示例场景:加入maxSize=3时,有新元素添加,此刻正回收1元素,刚好页面又使用1元素。这时候如果1元素被回收,就会找不到1元素从而崩溃。所以设计了活动缓存

增加的活动缓存区解决上面的问题,画图方便理解:

总结:1、当元素在使用时,将从内存缓存(二级缓存)移动到活动缓存(一级缓存);

2、当元素未使用时,将从活动缓存释放资源,然后把该元素从活动缓存移动到内存缓存;

三级缓存策略的使用总结:

1、优先从活动缓存读取
2、活动缓存没有,再内存缓存中读取
3、内存缓存没有,再去磁盘缓存读取
4、磁盘缓存没有,再去网络获取本地文件读取

Glide源码分析以及三级缓存原理相关推荐

  1. SSM源码分析之Spring05-DI实现原理(基于Annotation 注入)

    目录导航 前言 注解的引入 AnnotationConfigApplicationContext 对注解Bean初始化 AnnotationConfigApplicationContext注册注解Be ...

  2. okhttp配置缓存策略_Okhttp缓存源码分析以及自定义缓存实现

    原标题:Okhttp缓存源码分析以及自定义缓存实现 昨日,南京市公安局官方微博"平安南京"发布公告称,钱宝实际控制人张小雷因涉嫌违法犯罪于26日向当地警方投案自首.消息一出,迅速引 ...

  3. 10年大厂程序员是如何高效学习使用redis的丨redis源码分析丨redis存储原理

    10年大厂程序员是怎么学习使用redis的 1. redis存储原理分析 2. redis源码学习分享 3. redis跳表和B+树详细对比分析 视频讲解如下,点击观看: 10年大厂程序员是如何高效学 ...

  4. Android 图片框架原理——Glide源码分析

    目录 前言 一.With() 二.load() 三.into() 1. buildImageViewTarget() 2.关注1 :buildRequest 构建 3.关注2:runRequest 执 ...

  5. 源码分析Dubbo监控中心实现原理

       Dubbo监控的实现基本原理就是在服务调用时收集服务调用并发度.服务响应时间,然后以一定频率向监控中心汇报统计数据.    1.源码分析MonitorFilter过滤器 过滤器作用    监控过 ...

  6. YYImage实现思路源码分析(图片解压缩原理)

    YYKit组件之一---->YYImage 图像处理 移动端图片格式调研 图片处理的小技巧 YYWebImage源码分析 YYModel源码分析 YYText源码分析 核心思路--->图片 ...

  7. ceph bluestore 源码分析:刷缓存(trim)逻辑

    环境 ceph版本:12.2.1 部署模式:ec 2+1 osd: 3个 且资源池已经有数据 执行命令:ceph daemon osd.0 flush_store_cache 进行刷缓存.即将dump ...

  8. 【转】ABP源码分析十三:缓存Cache实现

    ABP中有两种cache的实现方式:MemoryCache 和 RedisCache. 如下图,两者都继承自ICache接口(准确说是CacheBase抽象类).ABP核心模块封装了MemoryCac ...

  9. 【golang源码分析】chan底层原理——附带读写用户队列的环形缓冲区

    1 环形缓冲区 1.1 环形缓冲区结构 环形缓冲区通常有一个读指针和一个写指针.读指针指向环形缓冲区中可读的数据,写指针指向环形缓冲区中可写的缓冲区.通过移动读指针和写指针就可以实现缓冲区的数据读取和 ...

  10. vuex 源码分析_深入Vuex原理(上)

    原标题:深入Vuex原理(上) 孔维国,2016年加入去哪儿网技术团队.目前在大住宿事业部/增值业务研发中心,参与开发了TMC.CRM.QTA.Auth等项目,负责node框架nomi的设计以及开发. ...

最新文章

  1. ABP源码分析三十四:ABP.Web.Mvc
  2. Xcode6中添加pch文件
  3. linux笔记_文件搜索命令
  4. java让服务器停止运行,java调用远程服务器的shell脚本以及停止的方法实现
  5. 790页微软官方《.Net核心编程》高清版PDF,提供下载
  6. 【剑指offer】最小的K个数
  7. Linux常用命令介绍(一)——文件与文件夹操作相关命令
  8. 使用ESP8266模块在WIFI下通过网页远程控制LED开关
  9. 【华人学者风采】忻获麟 加州大学欧文分校
  10. 电脑怎么安装xp系统原版镜像
  11. 微信开发、申请微信号测试账号
  12. selenium登录163邮箱,得到cookie,requests后续请求
  13. PCAP学习笔记二:pcap4j源码笔记
  14. 基于optix的习惯化渲染
  15. Unity 1.Roll a Ball
  16. 中国建筑设计行业市场调查研究及发展前景展望报告(2022-2028年)
  17. C盘瘦身:QQ文件的清理及Group2文件夹
  18. Android通过MediaStore获取音乐文件信息的方法
  19. iOS——SDWebImage解读
  20. dart 遍历数组_flutter开发,Dart中的那些骚气语法!

热门文章

  1. Excel表格-数据统计
  2. 2020级CHD新生训练题题解
  3. 解决webView不支持网页input type=“file“上传功能。接个文章搜索,自己写的代码,确保可以使用。
  4. 图表数据分析怎么做,举实例给你说清楚
  5. VScode 删除远程资源管理器中SSH TARGETS
  6. Ubuntu 下查看图片
  7. JavaScript绘制矢量图
  8. 数据分析 - 数据可视化图表 适用场景(学习笔记)
  9. 浅析 HLS 流媒体协议
  10. 冯言冯语说DSP(二)序列的z变换