https://juejin.im/post/6844903951897198605
https://blog.csdn.net/sinyu890807/column/info/15318
https://www.jianshu.com/p/317b2d6bde1b
前言
而内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,硬盘缓存则是防止应用重复从网络或其他地方重复下载和读取数据。
1、依赖
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
2、使用
Glide.with(this)
.load(URL_IMAGE)
.placeholder(R.drawable.ic_loading)
.error(R.drawable.ic_load_error)
.into(imageView);
上面只是 Glide.with(Activity act) 的流程,那么上面涉及到的这几个类,他们的职责主要干什么的,我们来看看。
-
Glide
主要做一些 init 工作,比如缓存,线程池,复用池的构建等等。 -
RequestManagerRetriever
主要是获得一个 RequestManager 请求管理类,然后绑定一个 Fragment 。 -
SupportRequestManagerFragment
用于管理请求的生命周期。 -
RequestManager
主要用于对请求的管理封装。
Glide的缓存设计可以说是非常先进的,考虑的场景也很周全。在缓存这一功能上,Glide又将它分成了两个模块,一个是内存缓存,一个是硬盘缓存。
这两个缓存模块的作用各不相同,内存缓存的主要作用是防止应用重复将图片数据读取到内存当中,而硬盘缓存的主要作用是防止应用重复从网络或其他地方重复下载和读取数据。
内存缓存和硬盘缓存的相互结合才构成了Glide极佳的图片缓存效果,那么接下来我们就分别来分析一下这两种缓存的使用方法以及它们的实现原理。
一、运行时缓存
Glide 的运行时缓存有 2 个缓存,一个 Map + 弱引用,一个 LRU 内存缓存。
1、活动缓存(保存正在使用的图片,非Lru缓存)
ActiveResources:如果当前对应的图片资源是从内存缓存中获取的,那么会将这个图片存储到活动资源中。当时用时引用计数加1,释放时引用计数减1,当引用计数为0时从内存资源获取,将内存缓存加载到活动缓存,remove内存缓存。
ActiveResources就是一个弱引用的 HashMap ,用来缓存正在使用中的图片,使用ActiveResources来缓存正在使用中的图片,可以保护正在使用的这些图片不会被 LruCache 算法回收掉,避免APP崩溃 。
2、内存缓存
LruResourceCache: 图片解析完成并最近被加载过,则放入内存中。
1、获取活动资源
public synchronized <R> LoadStatus load(
....//参数
) {
//1\. 优先加载内存中的活动缓存 - ActiveResources
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
// 如果找到获取资源,就返回上层
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
...
}
@Nullable
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable{
if (!isMemoryCacheable) {
return null;
}
// 通过 活动资源的 get 函数拿到活动资源的缓存
EngineResource<?> active = activeResources.get(key);
if (active != null) {
//正在使用,引用计数 +1
active.acquire();
}
return active;
}
final class ActiveResources {
private final boolean isActiveResourceRetentionAllowed;
private final Executor monitorClearedResourcesExecutor;
@VisibleForTesting
final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
private final ReferenceQueue<EngineResource<?>> resourceReferenceQueue = new ReferenceQueue<>();
private ResourceListener listener;
private volatile boolean isShutdown;
@Nullable
private volatile DequeuedResourceCallback cb;
...
//外部调用 get 函数拿到活动资源缓存
@Nullable
synchronized EngineResource<?> get(Key key) {
//通过 HashMap + WeakReference 的存储结构
//通过 HashMap 的 get 函数拿到活动缓存
ResourceWeakReference activeRef = activeEngineResources.get(key);
if (activeRef == null) {
return null;
}
EngineResource<?> active = activeRef.get();
if (active == null) {
cleanupActiveReference(activeRef);
}
return active;
}
//继承 WeakReference 弱引用,避免内存泄漏。
@VisibleForTesting
static final class ResourceWeakReference extends WeakReference<EngineResource<?>> {
....
}
通过上面代码我们知道活动缓存是 Map + WeakReference 来进行维护的,这样做的好处是避免图片资源内存泄漏。
2、存储活动资源
Engine#load函数中
//1. 如果活动缓存中没有,就加载 LRU 内存缓存中的资源数据。
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
// 加载内存中图片资源
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
//2. 拿到内存缓存,内部将当前的 key 缓存 remove 了
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
// 3. 如果内存缓存不为空,则引用计数器 +1
cached.acquire();
//4. 添加进活动缓存中
activeResources.activate(key, cached);
}
return cached;
}
3、清理活动资源
class EngineJob<R> implements DecodeJob.Callback<R>,
Poolable {
....
@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {
synchronized (this) {
this.resource = resource;
this.dataSource = dataSource;
}
notifyCallbacksOfResult();
}
@Synthetic
void notifyCallbacksOfResult() {
.....
//回调上层 Engine 任务完成了
listener.onEngineJobComplete(this, localKey, localResource);
//遍历资源回调给 ImageViewTarget ,并显示
for (final ResourceCallbackAndExecutor entry : copy) {
entry.executor.execute(new CallResourceReady(entry.cb));
}
//这里是通知发出上层删除活动资源数据
decrementPendingCallbacks();
}
....
}
//重要的就是这里了
@Synthetic
synchronized void decrementPendingCallbacks() {
if (decremented == 0) {
if (engineResource != null) {
//如果不为空,那么就调用内部 release 函数
engineResource.release();
}
}
}
class EngineResource<Z> implements Resource<Z> {
...
void release() {
synchronized (listener) {
synchronized (this) {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
//这里每次调用一次 release 内部引用计数法就会减一,到没有引用也就是为 0 的时候 ,就会通知上层
if (--acquired == 0) {
//回调出去,Engine 来接收
listener.onResourceReleased(key, this);
}
}
}
}
...
}
总结下步骤:
- 第一次加载没有获取到活动缓存。
- 接着加载内存资源缓存,先清理掉内存缓存,在添加进行活动缓存。
- 第二次加载活动缓存已经存在。
- 当前图片引用为 0 的时候,清理活动资源,并且添加进内存资源。
- 又回到了第一步,然后就这样环环相扣。
二、磁盘缓存
磁盘缓存-资源类型
DiskLruCacheWrapper:被解码后的图片写入磁盘文件中
磁盘缓存-原始数据
DiskLruCacheWrapper:网络请求成功后将原始数据在磁盘中缓存
-
DiskCacheStrategy.NONE
表示不开启磁盘缓存 -
DiskCacheStrategy.RESOURCE
表示只缓存转换之后的图片。 -
DiskCacheStrategy.ALL
表示既缓存原始图片,也缓存转换过后的图片。 -
DiskCacheStrategy.DATA
表示只缓存原始图片 -
DiskCacheStrategy.AUTOMATIC
根据数据源自动选择磁盘缓存策略(默认选择)
RequestOptions options = new RequestOptions()
//加载成功之前占位图
.placeholder(mPlaceholderId == 0 ? R.mipmap.photo_detail_empty : mPlaceholderId)
//加载错误之后的错误图
.error(mErrorId == 0 ? R.mipmap.photo_detail_empty : mErrorId)
//指定图片的尺寸
.override(100,100)
//指定图片的缩放类型为fitCenter (等比例缩放图片,宽或者是高等于 ImageView的宽或者是高。是指其中一个满足即可不会一定铺满 imageview)
.fitCenter()
//指定图片的缩放类型为centerCrop (等比例缩放图片,直到图片的宽高都 大于等于ImageView的宽度,然后截取中间的显示。)
.centerCrop()
//不使用内存缓存
.skipMemoryCache(true)
//只缓存最终的图片
.diskCacheStrategy(DiskCacheStrategy.RESOURCE);
上面这 4 中参数其实很好理解,这里有一个概念需要记住,就是当我们使用 Glide 去加载一张图片的时候,Glide 默认并不会将原始图片展示出来,而是会对图片进行压缩和转换,总之就是经过种种一系列操作之后得到的图片,就叫转换过后的图片。而 Glide 默认情况下在硬盘缓存的就是
DiskCacheStrategy.AUTOMATIC
1、获取资源数据
如果在活动缓存、内存缓存中没有找数据,那么就重新开启一个 GlideExecutor 线程池在 DecodeJob run 执行新的请求,下面我们就直接来看 DecodeJob run 函数,跟着它去找 资源数据的加载:
class DecodeJob<R> implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable<DecodeJob<?>>,
Poolable {
...
@Override
public void run() {
...
try {
//如果取消就通知加载失败
if (isCancelled) {
notifyFailed();
return;
}
//1\. 执行runWrapped
runWrapped();
} catch (CallbackException e) {
...
}
}
...
}
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
//2\. 找到执行的状态
stage = getNextStage(Stage.INITIALIZE);
//3\. 找到具体执行器
currentGenerator = getNextGenerator();
//4\. 开始执行
runGenerators();
break;
...
}
}
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE: //3.1解码后的资源执行器
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE://原始数据执行器
return new DataCacheGenerator(decodeHelper, this);
case SOURCE://新的请求,http 执行器
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
三、磁盘缓存
1、存储
- 资源缓存是在把图片转换完之后才缓存;
- 原始数据是网络请求成功之后就写入缓存;
2、获取
- 资源缓存跟原始数据都是在
GlideExecutor线程池中,Decodejob中检查获取数据。
四、复用池(BitmapPool)
在 Glide 中,在每次解析一张图片为 Bitmap 的时候不管是内存缓存还是磁盘缓存,都会从其BitmapPool 中查找一个可被复用的 Bitmap ,之后在将此块的内存缓存起来。
注意:在使用复用池的时候,如果存在能被复用的图片会重复使用该图片的内存。 所以复用并不能减少程序正在使用的内存大小。Bitmap 复用,解决的是减少频繁申请内存带来的性能(抖动、碎片)问题。
五、缓存 key 生成
不管是内存缓存还是磁盘缓存,存储的时候肯定需要一个唯一 key 值。在 Engine 的 load 函数中进行对 key 的生成。
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public synchronized <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
....
//1. 生成缓存唯一 key 值,model 就是图片地址
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
....
}
...
}
class EngineKeyFactory {
//生成 key
@SuppressWarnings("rawtypes")
EngineKey buildKey(Object model, Key signature, int width, int height,
Map<Class<?>, Transformation<?>> transformations, Class<?> resourceClass,
Class<?> transcodeClass, Options options) {
return new EngineKey(model, signature, width, height, transformations, resourceClass,
transcodeClass, options);
}
}
class EngineKey implements Key {
...
@Override
public boolean equals(Object o) {
if (o instanceof EngineKey) {
EngineKey other = (EngineKey) o;
return model.equals(other.model)
&& signature.equals(other.signature)
&& height == other.height
&& width == other.width
&& transformations.equals(other.transformations)
&& resourceClass.equals(other.resourceClass)
&& transcodeClass.equals(other.transcodeClass)
&& options.equals(other.options);
}
return false;
}
@Override
public int hashCode() {
if (hashCode == 0) {
hashCode = model.hashCode();
hashCode = 31 * hashCode + signature.hashCode();
hashCode = 31 * hashCode + width;
hashCode = 31 * hashCode + height;
hashCode = 31 * hashCode + transformations.hashCode();
hashCode = 31 * hashCode + resourceClass.hashCode();
hashCode = 31 * hashCode + transcodeClass.hashCode();
hashCode = 31 * hashCode + options.hashCode();
}
return hashCode;
}
...
}
根据注释和代码可以看到传入的参数之多,主要是根据 url ,签名,宽高等,其内部重写了 hashCode,equals,来保证对象的唯一性。
EngineResource的release函数
class EngineResource<Z> implements Resource<Z> {
...
void release() {
synchronized (listener) {
synchronized (this) {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
//这里每次调用一次 release 内部引用计数法就会减一,到没有引用也就是为 0 的时候 ,就会通知上层
if (--acquired == 0) {
//回调出去,Engine 来接收
listener.onResourceReleased(key, this);
}
}
}
}
...
}
Glide
private static final String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";
private static final String TAG = "Glide";
private static volatile Glide glide;
private static volatile boolean isInitializing;
//管理线程池
private final Engine engine;
private final BitmapPool bitmapPool;
private final MemoryCache memoryCache;
private final BitmapPreFiller bitmapPreFiller;
private final GlideContext glideContext;
private final Registry registry;
private final ArrayPool arrayPool;
private final RequestManagerRetriever requestManagerRetriever;
private final ConnectivityMonitorFactory connectivityMonitorFactory;
private final List<RequestManager> managers = new ArrayList<>();
private MemoryCategory memoryCategory = MemoryCategory.NORMAL;
...
//从GlideBuilder中构建此对象
Glide(
@NonNull Context context,
@NonNull Engine engine,
@NonNull MemoryCache memoryCache,
@NonNull BitmapPool bitmapPool,
@NonNull ArrayPool arrayPool,
@NonNull RequestManagerRetriever requestManagerRetriever,
@NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
int logLevel,
@NonNull RequestOptions defaultRequestOptions,
@NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions,
@NonNull List<RequestListener<Object>> defaultRequestListeners,
boolean isLoggingRequestOriginsEnabled) {
this.engine = engine;
this.bitmapPool = bitmapPool;
this.arrayPool = arrayPool;
this.memoryCache = memoryCache;
this.requestManagerRetriever = requestManagerRetriever;
this.connectivityMonitorFactory = connectivityMonitorFactory;
...
glideContext =
new GlideContext(
context,
arrayPool,
registry,
imageViewTargetFactory,
defaultRequestOptions,
defaultTransitionOptions,
defaultRequestListeners,
engine,
isLoggingRequestOriginsEnabled,
logLevel);
}
GlideBuilder
public final class GlideBuilder {
private final Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions = new ArrayMap<>();
//管理线程池
private Engine engine;
//对象池(享元模式),这样做避免重复创建对象,对内存开销有一定效果
private BitmapPool bitmapPool;
//数组对象池
private ArrayPool arrayPool;
//网络请求的线程池
private GlideExecutor sourceExecutor;
//本地磁盘缓存的线程池
private GlideExecutor diskCacheExecutor;
//本地磁盘缓存的工厂
private DiskCache.Factory diskCacheFactory;
//内存缓存
private MemorySizeCalculator memorySizeCalculator;
//资源内存缓存
private MemoryCache memoryCache;
//网络连接监控的工厂
private ConnectivityMonitorFactory connectivityMonitorFactory;
private int logLevel = Log.INFO;
private RequestOptions defaultRequestOptions = new RequestOptions();
@Nullable
private RequestManagerFactory requestManagerFactory;
//加载图片动画的一个线程池
private GlideExecutor animationExecutor;
private boolean isActiveResourceRetentionAllowed;
@Nullable
private List<RequestListener<Object>> defaultRequestListeners;
private boolean isLoggingRequestOriginsEnabled;
//都是一些配置信息,用到了 开闭原则。
....
//开始构建
@NonNull
Glide build(@NonNull Context context) {
//实例化一个网络请求的线程池
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
//实例化一个本地磁盘缓存的线程池
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
//实例化一个加载图片动画的一个线程池
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
//实例化一个对图片加载到内存的一个计算
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
//实例化一个默认网络连接监控的工厂
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
//实例化一个 Bitmap 对象池
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
//如果池子里还有可用的,直接加入 最近最少使用的 LruBitmap 容器里
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
//如果池子已经满了,那么就装在 BitmapPoolAdapter
bitmapPool = new BitmapPoolAdapter();
}
}
//实例化一个数组对象池
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
//资源内存缓存
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
//磁盘缓存的工厂
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
//构建执行缓存策略跟线程池的引擎
if (engine == null) {
engine =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
if (defaultRequestListeners == null) {
defaultRequestListeners = Collections.emptyList();
} else {
defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
}
//实例化一个 RequestManagerRetriever 请求管理类
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
//实例化 Glide 的地方
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock(),
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled);
}
}
Glide.with(Activity) 主要做了 线程池 + 缓存 + 请求管理与生命周期绑定+其它配置初始化的构建
Glide 底层成网络请求
SourceGenerator
@Override
public boolean startNext() {
if (dataToCache != null) {
Object data = dataToCache;
dataToCache = null;
cacheData(data);
}
if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
return true;
}
sourceCacheGenerator = null;
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
////获取一个 ModelLoad 加载器
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
////使用加载器中的 fetcher 根据优先级加载数据
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
List<LoadData<?>> getLoadData() {
if (!isLoadDataSet) {
isLoadDataSet = true;
loadData.clear();
//从 Glide 注册的 Model 来获取加载器(注册是在 Glide 初始化的时候通过 registr .append()添加的)
List<ModelLoader<Object, ?>> modelLoaders = glideContext.getRegistry().getModelLoaders(model);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = modelLoaders.size(); i < size; i++) {
ModelLoader<Object, ?> modelLoader = modelLoaders.get(i);
//开始构建加载器
LoadData<?> current = modelLoader.buildLoadData(model, width, height, options);
//如果架子啊器不为空,那么添加进临时缓存
if (current != null) {
loadData.add(current);
}
}
}
return loadData;
}
首先拿到一个加载器的容器,加载器是在 Glide 初始化的时候 通过 Registry.append() 添加的,这里因为我们以网络链接举例的。所以,ModelLoad 的实现类是HttpGlideUrlLoader加载器,我们看下它的具体实现
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height@NonNull Options options) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
// spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
这里看到是返回的一个HttpUrlFetcher 给加载器。加载器我们拿到了,现在开始加载,返回到刚刚的源码,请看下面:
private static final String TAG = "HttpUrlFetcher";
private static final int MAXIMUM_REDIRECTS = 5;
@VisibleForTesting
static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory();
private static final int INVALID_STATUS_CODE = -1;
private final GlideUrl glideUrl;
private final int timeout;
private final HttpUrlConnectionFactory connectionFactory;
private HttpURLConnection urlConnection;
private InputStream stream;
private volatile boolean isCancelled;
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
//http 请求,返回一个 InputStream 输入流
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
//将 InputStream 以回调形式回调出去
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));
}
}
}
我们可以看到HttpURLConnection 作为Glide 底层成网络请求的。请求成功之后直接返回的是一个输入流,最后会通过 onDataReady 回调到 DecodeJob onDataFetcherReady 函数中。
LruCache基本原理
LRU算法就是当缓存空间满了的时候,将最近最少使用的数据从缓存空间中删除以增加可用的缓存空间来缓存新数据。
这个算分的内部有一个缓存列表,每当一个缓存数据被访问的时候,这个数据就会被提到列表尾部,每次都这样的话,列表的头部数据就是最近最不常使用的了,当缓存空间不足时,就会删除列表头部的缓存数据。
Content
如果你在某个Activity上正在加载着一张图片,结果图片还没加载出来,Activity就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了
注意细微区别是Activity传的参数的是Activity的FragmentManager,Fragment传的参数的是ChildFragmentManager










网友评论