Glide学习

article/2025/10/12 13:33:33

Glide框架学习

  • 介绍
  • with(生命周期)
  • into
  • 缓存
    • LRU缓存
    • 三级缓存
    • 为什么要有两种内存缓存
    • 加载顺序
    • 活动缓存为什么使用弱引用

介绍

常规方式:Glide.with(this).load(URL).into(imageView)
虽然with方法重载了很多个,我们可以传入不同的对象,但是终归会分为两种。
后面我们还可以设置各种存储方式,加载的图片也可以设置各种设置,基本的源码学习还是分为三部分。
with、load、into

with(生命周期)

1、分为两种情况,一种会创建空白Fragment,一种不会。
子线程调用的Glide.with和传入Application的不会,其他的会。

创建一个空白的Fragment贴上去

  @NonNullpublic RequestManager get(@NonNull FragmentActivity activity) {if (Util.isOnBackgroundThread()) {return get(activity.getApplicationContext());} else {Util.assertNotDestroyed(activity);FragmentManager fm = activity.getSupportFragmentManager();return supportFragmentGet(activity, fm);}}@NonNullpublic RequestManager get(@NonNull Fragment fragment) { // androidxif (Util.isOnBackgroundThread()) {return get(fragment.getContext().getApplicationContext());} else {FragmentManager fm = fragment.getChildFragmentManager();return supportFragmentGet(fragment.getContext(), fm);}}

将准备好的空白Fragment添加上去

  private RequestManager supportFragmentGet(@NonNull Context context,@NonNull FragmentManager fm) {// 1、从 FragmentManager 中获取 SupportRequestManagerFragment(空白)SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);// 2、从该 空白Fragment 中获取 RequestManagerRequestManager requestManager = current.getRequestManager();// 3、首次获取,则实例化 RequestManagerif (requestManager == null) { //这样做的目的是为了  一个Activity或Fragment 只能有一个 RequestManager// 3.1 实例化Glide glide = Glide.get(context);requestManager = new RequestManager(glide, current.getGlideLifecycle(), context);// 3.2 设置 Fragment 对应的 RequestManager    空白的Fragment<--->requestManagercurrent.setRequestManager(requestManager);}return requestManager;}

如何保证一个Activity或者Fragment只会有一个空白的Fragment呢?

 // 1、从 FragmentManager 中获取 SupportRequestManagerFragment@NonNullprivate SupportRequestManagerFragment getSupportRequestManagerFragment(@NonNull final FragmentManager fm) {SupportRequestManagerFragment current =(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);if (current == null) {//  1.2 尝试从【记录保存】中获取 Fragmentcurrent = pendingSupportRequestManagerFragments.get(fm);// 1.3 实例化 Fragmentif (current == null) {// 1.3.1 创建对象 空白的Fragmentcurrent = new SupportRequestManagerFragment();  // 1.3.2 【记录保存】映射关系 进行保存   第一个保障// 一个MainActivity == 一个空白的SupportRequestManagerFragment == 一个RequestManagerpendingSupportRequestManagerFragments.put(fm, current);// 1.3.3 提交 Fragment 事务fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();// 1.3.4 post 一个消息handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();}}return current;}

创建了空白Fragment之后存储到对应的集合里面

 @VisibleForTestingfinal Map<FragmentManager, SupportRequestManagerFragment> pendingSupportRequestManagerFragments =new HashMap<>();

在通过handler指令移除

  switch (message.what) {case ID_REMOVE_FRAGMENT_MANAGER:  // 移除 【记录保存】  1.3.5 post 一个消息android.app.FragmentManager fm = (android.app.FragmentManager) message.obj;pendingRequestManagerFragments.remove(fm); // 1.3.6 移除临时记录中的映射关系break;

1、上面几个方法的流程说明:首先从FragmentManager 查找对应的tag的空白Fragment,第一次肯定找不到。
2、尝试从缓存数组当中找一个,找不到,创建之前再判断一次,防止刚才遍历数组的时候已经有另外的代码创建了一次。子线程是不会创建空白Fragment,按照道理只会在主线程才会执行的代码,应该从上到下执行的代码不可能出现多线程情况,下面的代码会解释。
3、创建一个空白Fragment,添加到缓存数组,添加到FragmentManager 里面,通过事物提交。
事物提交内部的机制是一个handler,那么就说明它不是立即执行,有一定的延迟。
4、通过handler主线程发送一个消息,删除刚添加到缓存数组的空白Fragment。
5、后面进来的代码一检查可以从tag中拿到已经贴上去的空白Fragment,就不会再进入这个方法了。
这个是基本逻辑。

将空白的Fragment贴上去这个操作是事务提交,底层还是通过一个handler指令操作,那么会有一定的延迟,如果这个时候第二个创建操作的代码执行过来,从Tag中肯定是找不到的,从缓存数组中再判断一次是因为如果上一个创建指令还没完全执行完贴上空白的Fragment,那么数组里面的临时数据也还没删除,那么如果数组里有数据我们直接就不需要再执行一次了,这也是双重保障。

考虑得非常详细,UI线程理论上不用考虑多线程的情况,但是这里连发送一个handler这种极限非常少的时间也考虑到了。

这就是没有加锁对象的时候创建空白Fragment包装只会一对一的关键代码逻辑。

2、为什么创建一个空白Fragment贴上去?
这个问题其实有点傻,现在jpt的lifecycle监听生命周期方法就是来自Glide,看它的生命周期方法就知道了。

// andrid x  的 空白的Fragment  监听生命周期变化的
public class SupportRequestManagerFragment extends Fragment {private static final String TAG = "SupportRMFragment";private final ActivityFragmentLifecycle lifecycle;@Nullable private RequestManager requestManager;public SupportRequestManagerFragment() {this(new ActivityFragmentLifecycle());}@VisibleForTesting@SuppressLint("ValidFragment")public SupportRequestManagerFragment(@NonNull ActivityFragmentLifecycle lifecycle) {this.lifecycle = lifecycle;}public void setRequestManager(@Nullable RequestManager requestManager) {this.requestManager = requestManager;}@NonNullpublic ActivityFragmentLifecycle getGlideLifecycle() {return lifecycle;}@Nullablepublic RequestManager getRequestManager() {return requestManager;}@Overridepublic void onAttach(@NonNull Context context) {super.onAttach(context);// this.lifecycle.addListener(requestManager);}@Overridepublic void onDetach() {super.onDetach();}@Overridepublic void onStart() {super.onStart();lifecycle.onStart();}@Overridepublic void onStop() {super.onStop();lifecycle.onStop();}@Overridepublic void onDestroy() {super.onDestroy();lifecycle.onDestroy();}
}

into

这个环节不准备深入讲解,我还没有深入分析,给大家准备一张神图。

缓存

LRU缓存

Glide里面的内存缓存和磁盘缓存都是使用了LRU。
1、指定大小。
2、最近、最少使用原则。
内部是链表结构,最近使用的添加到链头,当满了的时候链尾且是使用次数最少的先移除。

三级缓存

Glide有三级缓存,分别是磁盘缓存,和两种内存缓存,一种强引用持有的LRU缓存,一种若引用持有的活动缓存。

为什么要有两种内存缓存

如果我只有一种LRU缓存,那么我正在使用当中的资源就有可能被移除当前LRU的缓存池且释放掉,那不就GameOver了。
如果只有活动缓存一种,这是弱引用创建的,都活不过下一次垃圾回收,那么这个缓存的效果就太弱了。
如果活动缓存类似LRU是强引用持有,那么就会太庞大了,达到一定程度还是要移除一些资源。

加载顺序

1、正在使用当中的存储于活动缓存,页面销毁则移动LRU缓存。
2、加载顺序:先从活动缓存找、再从LRU缓存找、最后找磁盘缓存、从网络加载
3、有runing队列和等待队列,因为存在页面加载了一半就被关闭进入其他页面,等待队列全部清空,加载当前界面要显示的队列到runing队列。
4、Glide判断重复的标准是很严格的,一种完全相同内容的图片,宽高、名字等等有一点点差距都认为是不同的图片。
5、加载完成存储到磁盘缓存同时加载到活动缓存。

活动缓存为什么使用弱引用

我们都知道弱引用持有的对象活不过下一次垃圾回收。
使用弱引用是防止发生内存泄露,即防止出现对象的内存申请了以后就一直释放不了的情况,如果出现太多就会产生内存不够用的情况。一旦使用强引用,该对象就很难被垃圾回收器进行回收了。

弱引用队列ReferenceQueue

activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));

场景:我一个页面recyclerView加载了很多的图片,那么滑动很多次,加载了很多,那些已经加载过的图片资源全部都在活动缓存了,如果我滑得比较多,recyclerView本身缓存的ViewHolder已经满了。
1、也就是看不见的ViewHolder有一些不持有活动缓存里面的图片资源了,强引用断开,这个时候这些是弱引用创建的活不过下一个垃圾回收。
2、那些ViewHolder被缓存的还会持有活动缓存的图片资源,有强引用,他们就不会被回收。

在页面销毁的时候活动缓存的图片会被移除,被LRU缓存强引用持有。活动缓存使用弱引用确保一个界面类似滑动列表这种加载非常多的图片,也不会因为强引用导致垃圾回收器无法回收。


http://chatgpt.dhexx.cn/article/imlthSls.shtml

相关文章

Glide讲解

目录 Glide简介Glide的优点Glide的生命周期Glide如何实现图片缓存的内存缓存实现原理磁盘缓存实现原理引入缓存的目的Glide缓存流程从内存缓存读取总结从磁盘缓存读取总结写入磁盘缓存写入内存缓存汇总 Glide源码总结图解with&#xff08;&#xff09;load&#xff08;&#xf…

Glide详解

现在Android上的图片加载框架非常成熟&#xff0c;从最早的老牌图片加载框架UniversalImageLoader&#xff0c;到后来Google推出的Volley&#xff0c;再到后来的新兴军Glide和Picasso&#xff0c;当然还有Facebook的Fresco。每一个都非常稳定&#xff0c;功能也都十分强大。但是…

Android Glide

1.Glide Glide是Google主导的图片加载开源库。它有很多优势&#xff1a; ①使用简单&#xff0c;链式调用。 ②支持多种图片格式&#xff0c;如Gif、WebP、缩略图、Video等。 ③支持生命周期集成。Glide可以感知调用页面的生命周期&#xff0c;根据Activity或Fragment的生命…

[软件更新]gladder2.0.3.3

介绍 gladder是一个Firefox插件&#xff0c;名字被解释为Great Ladder (Ladder for Great Firewall)&#xff0c;目标是帮助人们跨过Great Firewall访问境外被查封的网站。 安装 https://addons.mozilla.org/en-US/firefox/addon/2864 (点击页面中的Install Now按钮) 功能 * 自…

飞机游戏代码(JAVA)

&#xff2d;yGameFrame类: 主要的调用类 package sc.wh.game;import javax.swing.JFrame; import java.awt.Color; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import sc.wh.game.*; import java.awt.event.KeyAdapte…

基于java的拼图经典游戏(附代码)

拼图游戏是一款经典的益智游戏&#xff0c;游戏开始前图片被随机打乱&#xff0c;空块位于最右下角&#xff0c;玩家通过点击空块周围图片或者按键方式对图片和空块进行相互交换&#xff0c;直到所有图片都回到原位即为游戏胜利。 本次制作的拼图游戏运行界面如下&#xff1a;…

java推箱子游戏源代码_java实现推箱子小游戏(附源码)

先上效果图 可以通过AWSD进行移动和推箱子 自己弄出来的代码玩起来还是很有意思的。 代码一共是三个.java文件,代码内容如下所示 package ss; import java.awt.Graphics; import java.awt.Image; import java.awt.Point; import java.awt.event.KeyEvent; import java.awt.eve…

免费Java游戏源代码素材推荐

家人们&#xff0c;最近我找到了一个很好用的Java游戏源代码免费素材网站 资源贼多&#xff0c;重点是免费&#xff01;&#xff01;&#xff01;白嫖一时爽&#xff0c;一直白嫖一直爽&#xff0c;嘿嘿嘿&#xff01;&#xff01;&#xff01;感兴趣的可以进去看看 接下来就…

java连连看代码_java实现连连看游戏

本文实例为大家分享了java实现连连看游戏的具体代码,供大家参考,具体内容如下 代码会实现共享的,这个是截图 代码: package com.lr.bean; import java.util.Scanner; import java.util.Random; import com.lr.bean.Point; public class Link{public static void main(Strin…

JAVA版扫雷游戏,清晰易懂,注释多

这是一篇关于JAVA的扫雷游戏&#xff0c;所有的图片均用文字代替&#xff0c;代码可直接运行。 文章目录 开发环境一、下载方法二、运行效果展示三、代码部分1.代码如下 总结 开发环境 开发工具&#xff1a;eclipse2021-12 JDK版本&#xff1a;JDK15.0.1 一、下载方法 链接&a…

猜数字游戏(Java源代码)

游戏后台随机生成1-20之间的5个数&#xff0c;未猜中提示“未命中”&#xff0c;继续猜测&#xff0c;猜中提示“运气不错&#xff0c;猜中了”&#xff0c;并输出数据出现的第一次位置源代码&#xff1a; import java.util.Random; import java.util.Scanner;//游戏后台随机…

JAVA实现扫雷游戏

后记&#xff1a;经评论区提醒&#xff0c;发现有两个bug没考虑到&#xff0c;感谢大家的提醒 bug1&#xff1a;绘制雷的时候有可能把两个雷随机到同样的位置。解决方法是在绘制雷的for循环内&#xff0c;rRow和rCol生成后做一个检测即可&#xff1a; /* 绘制地雷 */private v…

Java抽奖小游戏(包含代码)

情景&#xff1a; 假如从50个数字中确定10个中奖号码。 中奖号码要从50个数字中随机产生&#xff0c;中奖号码不可以重复&#xff0c;并对中奖号码进行排序。 解题思路&#xff1a; 首先建立一个长度为n号码的号码库&#xff1a;建立一个数组存放k个中奖号码抽取k个中奖号码。…

贪吃蛇java游戏代码_java实现贪吃蛇游戏代码(附完整源码)

先给大家分享源码&#xff0c;喜欢的朋友点此处下载。 游戏界面 GUI界面 java实现贪吃蛇游戏需要创建一个桌面窗口出来&#xff0c;此时就需要使用java中的swing控件 创建一个新窗口 JFrame frame new JFrame("贪吃蛇游戏"); //设置大小 frame.setBounds(10, 10, 90…

JAVA 实现生命游戏

生命游戏的规则: 生命游戏中&#xff0c;对于任意细胞&#xff1a;每个细胞有两种状态&#xff1a;存活或死亡。每个细胞与以自身为中心的周围八格细胞产生互动。    1.当前细胞为存活状态时&#xff0c;当周围的活细胞低于2个时&#xff0c; 该细胞因孤独而死亡;    2.当…

Java五子棋全代码

用Java编写简单的五子棋 前言 这两天在空闲时间做了个五子棋项目&#xff0c;分享给大家看一下&#xff0c;界面是这样的&#xff1a;        呜呜呜&#xff0c;界面很丑我知道&#xff0c;本人虽有几年PS基础&#xff0c;但知识浅薄&#xff0c;审美观不尽人意&#xff…

五子棋小游戏 java版(代码+详细注释)

游戏展示 这周闲来无事&#xff0c;再来写个五子棋小游戏。基本功能都实现了&#xff0c;包括人人对战、人机对战。界面布局和功能都写的还行&#xff0c;没做到很优秀&#xff0c;但也不算差。如有需要&#xff0c;做个java初学者的课程设计或者自己写着玩玩也都是不错的&…

【Java实现小游戏】飞翔的小鸟(源码)

游戏玩法&#xff1a;通过鼠标点击使小鸟上下移动穿过柱子并完成得分&#xff0c;小鸟碰到柱子或掉落到地面上都会结束游戏。 &#xff08;游戏内图片&#xff09; 下面是实现这个游戏的代码&#xff1a; Brid类&#xff1a; package bird;import org.omg.CORBA.IMP_LIMIT;im…

用简单Java代码尝试在控制台写游戏(附源码)

尝试写了一个在Java控制台运行的代码游戏&#xff0c;由于写这个的时候&#xff0c;博主还没学到可视化界面&#xff0c;也没有学到面向对象&#xff0c;甚至没有集合&#xff0c;运用的全是之前C语言的语法&#xff0c;因此应该很容易看懂吧。末尾附上源码。 以下是效果展示 …

Java小游戏练习---超级玛丽代码实现

B站教学视频&#xff1a; 01_超级玛丽_创建窗口_哔哩哔哩_bilibili 素材提取&#xff1a; 【超级会员V2】我通过百度网盘分享的文件&#xff1a;Java游戏项目… 链接:百度网盘 请输入提取码 提取码:k6j1 复制这段内容打开「百度网盘APP 即可获取」 百度网盘 请输入提取码 百度…