文章目录
前言
Glide 4.11.0版本代码剖析
主线:
Glide.with(MainActivity.this).load(url).into(imageView);
总体认识这几个方法都实现了哪些功能:
with
通过一个空白的Fragment去管理生命周期,最终返回RequestManager对象load
构建出RequestBuilder对象给后面into方法使用into
实现了缓存机制- 等待队列和运行队列
- 活动缓存
- 内存缓存
- 磁盘缓存
- 网络请求
1.with方法
1.1 如何监听Glide的生命周期
Glide利用一个空白的Fragment去监听Activity的生命周期,然后可以根据生命周期的变化,对Glide加载图片的BitMap进行回收处理。
SupportRequestManagerFragment:
空白的Fragment,androidX
RequestManagerFragment:
空白的Fragment,android.app
RequestManagerRetriever:
通过这个类的get方法返回RequestManager对象
RequestManager:
对图片加载进行管理,比如:页面关闭时对图片BitMap进行销毁
1.2 生命周期作用域Application、Activity、Fragment
根据Glide.with(参数
)传入的参数
来决定作用域,不同的作用域生命周期不同。
- 如果在子线程使用Glide,不论传入什么参数,作用域是Application,Glide与应用的生命周期相同
不会创建一个空白的Fragment来监听生命周期
- 如果在主线程中使用Glide
会创建一个空白的Fragment来监听生命周期
- 传入的参数是view,那么作用域是Activity或Fragment
- 传入的参数是Fragment,作用域是Fragment
- 传入的参数是Activity,作用域是Activity
- 传入的参数是ServiceContext或ApplicationContext,作用域是Application,生命周期等同于App的生命周期,
同样不会创建一个空白的Fragment来监听生命周期
1.3 生命周期的绑定
将空白的Fragment绑定到RequestMananger上,RequestManagerRetriever.java
代码如下:
private RequestManager supportFragmentGet(){
//省略非核心代码
//获取到空白的Fragment
SupportRequestManagerFragment current =
getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
//将RequestManager绑定到空白的Fragment上,空白的Fragment监听到Activity生命周期时,
//空白Frag同时会执行RequestManager的与生命周期同名的方法
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
//通过工厂,创建RequestManager对象
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
//将RequestManager绑定到空白Fragment,空白Fragment生命周期发生改变时,
//RequestManager中会执行与生命周期同名的方法
current.setRequestManager(requestManager);
}
return requestManager;
}
/**
* 通过是Tag或者是Map集合获取到空白Fragment
**/
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
//通过Tag获取到空白Fragment
SupportRequestManagerFragment current =
(SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//从缓存的Map集合中获取空白Fragment
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
//通过Tag和Map缓存集合都没有获取到,则创建一个空白的Fragment
current = new SupportRequestManagerFragment();
//将创建的空白Fragment缓存到Map集合
pendingSupportRequestManagerFragments.put(fm, current);
//将空白Fragment添加到我们的Activity或Fragment中去
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//发送Handler消息,在主线程中移除Map缓存集合中的空白Fragment
//主要目的是保证主线程中,FragmentManager通过Tag获取到的空白Fragment非空
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
/**
* 接收发送过来的消息,移除Map缓存集合中的空白Fragment
**/
public boolean handleMessage(Message message) {
switch (message.what) {
case ID_REMOVE_FRAGMENT_MANAGER:
android.app.FragmentManager fm = (android.app.FragmentManager)
//从Map缓存集合中移除空白Fragment android.app包下的Fragment
removed = pendingRequestManagerFragments.remove(fm);
break;
case ID_REMOVE_SUPPORT_FRAGMENT_MANAGER:
FragmentManager supportFm = (FragmentManager) message.obj;
//从Map缓存集合中移除空白Fragment AndroidX下的Fragment
removed = pendingSupportRequestManagerFragments.remove(supportFm);
break;
}
return handled;
}
在绑定空白Fragment的时候,首先需要获取到空白的Fragment,可以通过Tag标签获取到已经添加到Activity上的空白Fragment,也可以通过Map缓存集合中获取到创建的空白Fragment,然后将空白的Fragment绑定到RequestManager身上。
注意:
RequestManager实现了LifeCycleListener接口, 是在构造函数中以Handler发送消息的方式注册到ActivityFragmentLifeCycle中去的。所以,当页面生命周期发生变化的时候,会执行RequestManager中的与生命周期同名的方法。
1.4 生命周期的监听
当添加到Activity中的空白Fragment在创建的时候,会创建一个ActivityFragmentLifecycle作为参数传入,在Activity生命周期发生变化的时候,里面的空白Fragment就会监听到Activity生命周期的变化,同时会执行相同的生命周期方法,此时会执行我们传入的ActivityFragmentLifecycle对象的与生命周期同名的方法,而这些方法在执行的时候,就会遍历一个存储了LifeCycleListener接口的集合,然后执行集合中每一个接口实现类的与生命周期同名的方法。
解释说明:
Activity
- 举例:MainActivity
空白Fragment
- 举例:继承自androidx的SupportRequstManagerFragment
ActivityFragmentLifecycle
- LifeCycle接口的实现类,这个接口还有另外一个实现类ApplicationLifycycle
- 这个类中维护了一个LifeCycleListener接口的集合,通过addListener将接口的实现类添加到集合,通过removeListener将接口的实现类移出集合。
LifeCycleListener接口的集合源码如下:
private final Set<LifecycleListener> lifecycleListeners = Collections.newSetFromMap(new WeakHashMap<LifecycleListener, Boolean>());
LifeCycleListener
- 该接口的典型的实现类有:RequestManager、ImageViewTarget、BitmapImageViewTarget等;
- 通过LifeCycle实现类ActivityFragmentLifeCycle的addListener方法,将这些实现类添加到上面的接口集合中去,当Activity发生onStart、onStop和onDestroy生命周期变化的时候,就会执行这些实现类中的与生命周期同名的方法。
1.5生命周期的回调
LifeCycleListener只实现了3个方法,分别是onStart、onStop和onDestroy,当页面执行与以上几个方法同名的生命周期方法时,就会回调到注册进入LifeCycleListener接口集合的实现类的onStart、onStop和onDestroy方法。
这里只用onStart()生命周期方法作为入口举例,生命周期的回调具体流程如下:
- Activity执行onStart生命周期方法
- 举例:
MainActivity.onStart()
- 举例:
- ->空白Fragment执行onStart生命周期方法
- 举例:
SupportRequestManagerFragment.onStart()
- 举例:
- ->传入空白Fragment构造函数中的ActivityFragmentLifeCycle执行onStart方法
- 举例:
ActivityFragmentLifeCycle.onStart()
- 举例:
- ->ActivityFragmentLifeCycle类中,遍历LifeCycleListener集合,分别执行实现类的onStart方法
- 举例:
RequestManager.onStart()
- 举例:
2.load方法
创建出RequestBuilder对象,供into方法使用。
流程如下:
load
(“https://img-blog.csdnimg.cn/5c2b98c8029f4ff5ac3e178d2c0a0940.png”) //加载网络图片url,不过这里并没有真正去加载- ->
asDrawable()
@RequestManager.java - ->
as(Drawable.class)
@RequestManager.java - ->
new RequestBuilder<>(glide, this, resourceClass, context)
@RequestManager.java
3.into方法
3.1 into方法的源码中流程走向
into的源码走向,这是一个极其复杂的流程,需要查阅Glide的into方法
时序图
,才能完整走通整个流程。
尝试走通into方法的整个流程:
- into(ImageView view) @RequestBuilder
- into(glideContext.buildImageViewTarget(view, transcodeClass), null, requestOptions, Executors.mainThreadExecutor()) @RequestBuilder
该方法最终返回BitmapImageViewTarget对象
- buildImageViewTarget()
- 返回BitmapImageViewTarget或DrawableImageViewTarget对象,我们这是是返回
BitmapImageViewTarget
- 返回BitmapImageViewTarget或DrawableImageViewTarget对象,我们这是是返回
- Executors.mainThreadExecutor()
创建了一个Executor(并且这个Executor中包含了一个主线程的handler),这个Executor在把EngineJob当做线程池执行一个DecodeJob任务时会用到,然后就会执行DecodeJob的run方法,用来从从磁盘缓存中取出图片文件
- Request request = buildRequest(target, targetListener, options, callbackExecutor);
- 构建Request接口的实现类
SingleRequest
- 构建Request接口的实现类
- previous.begin();
- previous是我们前面构建出的Request接口的实现类
SingleRequest
,所以这里其实是调用的SingleRequest.begin()
- previous是我们前面构建出的Request接口的实现类
- buildImageViewTarget()
- SingleRequest.begin() @SingleRequest
- onResourceReady(resource, DataSource.MEMORY_CACHE);
- 完成后调用该方法,将资源回调回去
- onSizeReady(overrideWidth, overrideHeight);
- 计算好图片控件的宽高后调用该方法,没有计算好则需要通过如下方法去计算,target.getSize(this),计算好继续回调onSizeRead方法
- target.onLoadStarted(getPlaceholderDrawable());
- 这里就会开始加载图片了,target是上面的
BitmapImageViewTarget
,但是是在其父类ImageViewTarget
中完成的该方法处理,ImageViewTarget.onLoadStarted()
- 这里就会开始加载图片了,target是上面的
- onResourceReady(resource, DataSource.MEMORY_CACHE);
- onSizeReady(overrideWidth, overrideHeight); @SingleRequest
- 图片控件的宽高都计算好了
- engine.load() @SingleRequest
- load() @Engine
- EngineKey key = keyFactory.buildKey()
构建出EngineKey,这个对象中包含了图片的宽、高、签名等信息,通过这些信息可以作为找到存储图片的唯一标识
- memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);//从缓存中加载图片资源
- EngineResource<?> active = loadFromActiveResources(key);
- 从活动缓存中加载图片资源
- EngineResource<?> cached = loadFromCache(key);
- 活动缓存中没有找到图片资源,则从内存缓存中加载图片资源
- EngineResource<?> active = loadFromActiveResources(key);
- waitForExistingOrStartNewJob()
活动缓存和内存缓存中都没有查找到图片资源,则需要通过线程池从磁盘缓存中去查询图片资源
- EngineKey key = keyFactory.buildKey()
- waitForExistingOrStartNewJob() @Engine
- EngineJob engineJob = engineJobFactory.build()
- 查找有没有正在运行的任务,里面包含了线程池来执行任务
- DecodeJob decodeJob = decodeJobFactory.build()
- 这就是一个Runnable任务
- engineJob.start(decodeJob);
- (GlideExecutor)executor.execute(decodeJob);
- //利用线程池来执行任务
- (GlideExecutor)executor.execute(decodeJob);
- EngineJob engineJob = engineJobFactory.build()
- run() @DecodeJob
- 执行线程池中任务的run方法
- runWrapped() @DecodeJob
- currentGenerator = getNextGenerator();
- 返回
SourceGenerator
对象,也就是说
currentGenerator = SourceGenerator
- 返回
- currentGenerator = getNextGenerator();
- runGenerators() @DecodeJob
- currentGenerator.startNext()
- 由于前面将currentGenerator赋值给了SourceGenerator,所以这里其实是调用的
SourceGenerator.startNext()
- 由于前面将currentGenerator赋值给了SourceGenerator,所以这里其实是调用的
- startNext() @SourceGenerator
helper.getLoadData().get(loadDataListIndex++)
; @SourceGeneratormodelLoader.buildLoadData
(model, width, height, options); @SourceGenerator- 在Glide构造函数中对modelLoader的对象进行了实现,所以这里是调用的
HttpGlideUrlLoader.buildLoadData()
- buildLoadData() @HttpClideUrlLoader
- new HttpUrlFetcher(url, timeout)
- 也就是说,前面我们的
helper.getLoadData().get(loadDataListIndex++)
得到的LoadData<Data>
类中的DataFetcher<Data> fetcher
参数,最终的实现类是HttpUrlFetcher
- 也就是说,前面我们的
- new HttpUrlFetcher(url, timeout)
- 在Glide构造函数中对modelLoader的对象进行了实现,所以这里是调用的
- startNextLoad(loadData); @SourceGenerator
- loadData.fetcher.loadData() @SourceGenerator
- 这里的loadData.fetcher就是前面返回的
HttpUrlFetcher
对象所以最终执行的是HttpUrlFetcher.loadData()
- 这里的loadData.fetcher就是前面返回的
- loadData() @HttpUrlFetcher
- InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); @@HttpUrlFetcher
这里面就会通过HttpUrlConnection去访问网络,最终返回图片的InputStream流
- callback.onDataReady(result);
将返回的InputStream流回调回去,这里需要一层层回调上去,解析然后展示在控件上
- InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders()); @@HttpUrlFetcher
通过HttpUrlConnection访问网络图片地址,拿到图片的InputStream后一层层往上回调流程如下:
- callback.onDataReady(result); @HttpUrlFetcher
- onDataReady(Object data) @ResourceCacheGenerator
- cb.onDataFetcherReady(
sourceKey, data, loadData.fetcher, DataSource.RESOURCE_DISK_CACHE, currentKey); @ResourceCacheGenerator
- cb.onDataFetcherReady(
- onDataFetcherReady() @SourceGenerator
- cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey); @SourceGenerator
- onDataFetcherReady() @DecodeJob
- decodeFromRetrievedData(); @DecodeJob
- decodeFromData(currentFetcher, currentData, currentDataSource); @DecodeJob
- decodeFromFetcher(data, dataSource); @DecodeJob
- runLoadPath(data, dataSource, path); @DecodeJob
- path.load(rewinder, options, width, height, new DecodeCallback(dataSource)); @DecodeJob
- loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables); @LoadPath
- result = path.decode(rewinder, width, height, options, decodeCallback); @LoadPath
- Resource decoded = decodeResource(rewinder, width, height, options);
通过解码,最终:decoded = Resource<Bitmap>
- decodeResourceWithList(rewinder, width, height, options, exceptions);
- result = decoder.decode(data, width, height, options);
- 这里会调用实现类StringBitmapDecode.decode方法
- Resource decode(InputStream source, int width, int height, Options options) @StringBitmapDecode
最终返回Bitmap,就将InputStream最终转换成Bitmap了,可以在控件中展示
- Resource transformed = callback.onResourceDecoded(decoded); @LoadPath
- 将上面解码出来的Bitmap回调出去,
decoded = Resource<Bitmap>
- 将上面解码出来的Bitmap回调出去,
- Resource onResourceDecoded(Resource decoded) @DecodeJob
- onResourceDecoded(dataSource, decoded); @DecodeJob
callback.onResourceDecode
//解释一下,这里有点懵逼了- DecodeJob是一个Runnale任务,会执行run方法
- runWrapped(); @DecodeJob
- decodeFromRetrievedData();
- notifyEncodeAndRelease(resource, currentDataSource);
- notifyComplete(result, dataSource);
- callback.onResourceReady(resource, dataSource);
- notifyCallbacksOfResult(); @EngineJob
- engineJobListener.onEngineJobComplete(this, localKey, localResource); @EngineJob
将资源添加到活动缓存
- onEngineJobComplete() @Engine
activeResources.activate(key, resource);//将资源添加到活动缓存
- onEngineJobComplete() @Engine
- entry.executor.execute(new CallResourceReady(entry.cb));
线程池执行任务CallResourceReady,会运行里面的run方法
- callCallbackOnResourceReady(cb);
- engineJobListener.onEngineJobComplete(this, localKey, localResource); @EngineJob
- callCallbackOnResourceReady() @EngineJob
- cb.onResourceReady(engineResource, dataSource);
- onResourceReady(Resource<?> resource, DataSource dataSource) @SingleRequest
- onResourceReady((Resource) resource, ® received, dataSource);
- target.onResourceReady(result, animation);
这里的target就是BitmapImageViewTarget对象,他的父类ImageViewTarget中包含了这个方法
- onResourceReady() @ImageViewTarget
- setResourceInternal(resource);
- setResource(resource);
- 由其子类BitmapImageViewTarget实现这个抽象方法
- setResource(Bitmap resource) @BitmapImageViewTarget
- 收工!收工!收工!
可以通过下面这张图了解into在整个过程中,做了哪些处理:
这张图参考博客地址:
http://www.javashuo.com/article/p-vigwftpx-kg.html
3.2 Glide活动缓存的由来
活动缓存activeResources的存在,是因为如果App中内存缓存LruCacheResources中存储了过多的图片资源,会导致App占用的内存急剧增大,所以LruCacheResources中会利用LRU算法清理最近最不常用的图片资源,这个过程中可能会清理掉当前页面展示的图片的内存资源,从到导致App崩溃无法正常使用。所以,在内存缓存和App中间,会存在一个活动缓存ActiveResources,用来存放当前正在使用的图片的内存资源,这个活动缓存没有大小限制,随着页面的关闭而清空,清空之间将活动缓存中的图片资源添加到LruCacheResources内存缓存中去。
4.Glide缓存机制
4.1 资源封装
- 当我们的页面需要展示一张图片时,首先会从活动缓存中去查询图片资源是否存在,存在则直接加载活动缓存中的图片资源;
- 当活动缓存中不存在要加载的图片资源时,则从Lru内存缓存中去查询图片资源是否存在,存在则将Lru内存缓存中的图片资源移动到活动缓存,然后加载活动缓存中的图片资源;
- 当Lru内存缓存中不存在要加载的图片资源时,则从磁盘缓存中去查询图片资源是否存在,存在则将磁盘缓存中的图片资源复制一份到活动缓存,然后加载活动缓存中的图片资源;
- 当磁盘缓存中不存在要加载的图片资源时,则从网络去下载图片资源,然后保存到磁盘缓存,然后从磁盘缓存中复制一份到活动缓存,然后从活动缓存中加载图片资源。
- 当页面销毁时,图片资源就会从活动缓存中移动到Lru内存缓存;当App被杀掉时,活动缓存和Lru内存缓存中的图片资源都将被清除,图片资源最终会存在磁盘缓存中。
知识拓展:
Lru内存缓存和活动缓存都属于内存缓存,App被杀死后,内存缓存都会被清除;磁盘缓存属于硬盘缓存,会通过Lru算法进行资源清理。
磁盘缓存和Lru内存缓存都采用了Lru算法,所以我们来了解一些其中的Lru算法机制。
LruCache是一个Lru算法的类,里面通过使用LinkedHashMap来实现Lru算法机制,LinkedHashMap使用案例如下:
/**
* 测试Lru算法,最近最少使用原则
**/
private static void testLruCache() {
//int initialCapacity, float loadFactor, boolean accessOrder
//参数1:集合初始容量 参数2:加载因子,达到容量的多少后会扩容
//参数3:是否进行使用后排序,使用过则拍在最后面,排在前面的是最近最少使用的
LinkedHashMap linkedHashMap = new LinkedHashMap(0, 0.75f, true);
//依次按照1,2,3的顺序添加到集合;最先添加的元素最先获取到,最后添加的元素最后获取到
System.out.println("最先添加的元素最先获取到,最后添加的元素最后获取到");
linkedHashMap.put(1, "1");
linkedHashMap.put(2, "2");
linkedHashMap.put(3, "3");
System.out.println(linkedHashMap.toString());
//最先添加的元素被使用一次,会放在集合中最后面,就会最后获取到该元素
System.out.println("最先添加的元素被使用一次,就会最后获取到该元素");
linkedHashMap.get(1);
System.out.println(linkedHashMap.toString());
System.out.println();
}
打印日志:
> Task :javalib:JavaTest.main()
最先添加的元素最先获取到,最后添加的元素最后获取到
{1=1, 2=2, 3=3}
最先添加的元素被使用一次,就会最后获取到该元素
{2=2, 3=3, 1=1}
所谓资源封装,就是要封装获取图片资源的key,还有图片资源bitmap本身;当我们需要从活动缓存、Lru内存缓存和磁盘缓存中获取缓存的图片资源时,就需要通过这个唯一的key去查找bitmap图片资源。
4.2 活动缓存
活动缓存是一个Map集合,key就是我们上面资源封装出来的那个key,value就是包含了bitmap的图片资源;需要包含添加和删除操作,根据key、value添加图片资源,根据key从活动缓存中移除图片资源;同时还需要包含一个接口回调函数,因为在空白Fragment监听到页面销毁的生命周期方法时,会将当前页面正在使用的图片资源从活动缓存中删除,然后将图片资源移动到Lru内存缓存中。
4.3 内存缓存
内存缓存只需要直接继承自LruCache,无需实现LruCache实现的容器LinkedHashMap,还有里面包含的put和get方法,但是需要重写sizeOf()方法,重写计算集合中每一个缓存资源bitmap的大小。
4.4 磁盘缓存
DiskLruCache是由Jake Warton开发的,存在与git上的项目。
这个类同样也是继承自LruCache类,只不过数据不保存在缓存,而是通过io流的形式,保存到磁盘。
注意,我们是通过图片的url作为key,并且当做缓存资源文件的名称,但是这个key必须要加密,否则就是带有图片url的那种斜杠和冒号之类的格式,在文件路径中这属于不合格的格式。所以需要转换将这个数据进行转换,然后将bitmap数据存储在磁盘缓存文件中;然后我们就可以根据图片的key获取到磁盘中缓存文件中图片的bitmap数据。
4.5 缓存加载流程
几种情况下,缓存资源的加载过程分析:
- 情况1
- 第一次加载图片,是通过网络去下载图片,保存到磁盘缓存中,然后从磁盘缓存中获取到图片资源,再保存一份到活动缓存,然后从活动缓存中获取到图片资源,展示到图片控件上;
- 第二次加载图片时,直接从活动缓存中获取到图片资源,展示到图片控件上;
- 第三次加载图片时,同样是从活动缓存中获取到图片资源,展示到图片控件上;
- 第N次加载图片,都是从活动缓存中获取到图片资源,展示到图片控件上。
- 情况2
- 将当前Activity关闭,页面中的活动缓存资源会被释放,缓存的图片资源会被移动到内存缓存中去;
- 然后再次打开当前Activity,会从内存缓存中获取到图片资源,然后将内存缓存中的图片资源移动到活动缓存中,然后从活动缓存中获取到图片资源,展示到图片控件上;
- 下一次加载图片资源,会从活动缓存中获取到图片资源,展示到图片控件上;
- 第二次加载图片时,直接从活动缓存中获取到图片资源,展示到图片控件上;
- 第三次加载图片时,同样是从活动缓存中获取到图片资源,展示到图片控件上;
- 第N次加载图片,都是从活动缓存中获取到图片资源,展示到图片控件上。
- 情况3
- 将App杀掉,整个活动缓存和内存缓存都被清除掉了,因为他们都是运行时缓存;
- 首次启动App,打开当前页面,会从磁盘缓存中获取到图片资源,再保存一份到活动缓存,然后从活动缓存中获取到图片资源,展示到图片控件上;
- 第二次加载图片时,直接从活动缓存中获取到图片资源,展示到图片控件上;
- 第三次加载图片时,同样是从活动缓存中获取到图片资源,展示到图片控件上;
- 第N次加载图片,都是从活动缓存中获取到图片资源,展示到图片控件上。
Glide整体流程
Glide整体流程图-简化版
面试题
1.在使用Glide的过程中,什么情况下会导致内存溢出,说说原因?
在子线程中使用Glide或者with方法传入的参数是Application的上下文时,都会存在内存溢出的风险。因为在这两种情况下,Glide的生命周期与App的生命周期相同,当我们在页面销毁的时候,有图片还没有加载完成,就会存在内存泄露的风险。因为没有创建空白的Fragment监听页面生命周期,对未完成的任务进行处理。
我们平时使用Glide的with方法时,传入的参数应该是非Application作用域的上下文,可以传入当前Activity的上下文。这种情况下,Glide会为我们创建一个空白的Fragment,他可以监听到Activity或者是Fragment的生命周期,然后在页面销毁的时候,会通过这个空白的Fragment,来完成资源的销毁工作,或者是对下载的图片不予处理等。
2.Glide源码中的缓存,为什么要有两个,一个活动缓存,一个内存缓存?
注意:
活动缓存也叫活动资源、活跃缓存、前台缓存;内存缓存也叫LRU缓存。内存缓存(LRU缓存):
是用来存放我们打开App后,最近最常使用的图片资源的内存缓存集合。为了防止App加载的图片资源过多,导致占用的系统内存资源过大问题,采用了LRU算法,根据缓存最大值对最近最少使用到的资源进行清理。但是在清理资源的过程中,可能会清理到我们当前页面展示过的资源,此时会导致应用崩溃,所以引出了活动缓存。活动缓存(活动资源、活跃资源、前台缓存):
是用来对正在使用的图片资源进行存储的内存缓存集合,这是一个没有采用过LRU算法的集合,所以不存在容量限制,不会对当前页面展示的资源进行清理。当我们的页面要展示图片时,先从活动缓存中去查找是否有图片资源,有则直接使用,没有则会去查找内存缓存,当内存缓存中存在我们的图片资源时,则将他移动到活动缓存,并从内存缓存中移除,然后再从活动缓存中取出使用;当我们的页面销毁时,需要将图片资源从活动缓存中移动到内存缓存中,同时删除活动缓存中的图片资源。