相关文章
Glide源码分析之一
Glide源码分析之二
Glide源码分析之三
into()方法做了什么
==关键词==:
Transform对图片进行裁剪操作之后进行转化
Target监控生命周期的
ModelLoader主要是负责从数据源中获取原始数据(imputStream)
DataFetcher主要是负责将数据源中的原始数据转化成各种我们所需要的不同的数据类型
RescourceTranscoder主要负责将获取到的原始数据(io流、数据流)解码成我们的bitmap,解码之后的资源称之为Rescource,RescourceTranscoder就是负责完成解码的对象
Engine负责任务创建,发起,回调,资源的管理
DataLoaderProvider是负责我们图片编解码。(是一个接口)data->rescource data->持久化到本地文件(磁盘缓存)
ImageVideoModelLoader非常重要
into()方法绝对是Glide里面逻辑最为复杂的地方(所有跟ui相关的都在这个方法进行),没有之一,下面我们来分析一下。
into()方法中并没有任何逻辑,只有一句super.into(view)。那么很显然,into()方法的具体逻辑都是在DrawableRequestBuilder的父类当中了。
public Target<TranscodeType> into(ImageView view) {
//判断是不是主线程
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
//center_crop的意思是将我们的图片尺寸按照imageview的尺寸进行适当缩放展示中心区域
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
//关键句 上面的代码主要是图片处理
return into(glide.buildImageViewTarget(view, transcodeClass));
}
这里讲到了Util.assertMainThread();方法,在之前的with和into里都没有见过这个方法。其实从名字我们已经可以看出一二:
/**
* Throws an {@link java.lang.IllegalArgumentException} if called on a thread other than the main thread.
*/
public static void assertMainThread() {
if (!isOnMainThread()) {
throw new IllegalArgumentException("You must call this method on the main thread");
}
}
如果不是主线程则会抛出异常,其实可以表明我们glide从into()开始所有图片的操作都会在主线程中进行。
继续看into()方法,其实前面的逻辑可以先不用管,从case的名字也可以看出大概就是图片处理的方式,主要看最后一行into(glide.buildImageViewTarget(view, transcodeClass));他分为两步:
- glide.buildImageViewTarget(view, transcodeClass)构建出一个Target对象用来最终展示图片;
```
<R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
}
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
//其他情况都是GLideDrawableImageViewTarget(正常加载或者asGif())
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
//如果你使用的是asBitmap()方法则会新建一个BitmapImageViewTarget
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
```
这个class参数其实基本上只有两种情况,
- 如果你在使用Glide加载图片的时候调用了asBitmap()方法,那么这里就会构建出BitmapImageViewTarget对象,
- 否则的话构建的都是GlideDrawableImageViewTarget对象。
至于上述代码中的DrawableImageViewTarget对象,这个通常都是用不到的,我们可以暂时不用管它。
通过glide.buildImageViewTarget()方法,我们构建出了一个GlideDrawableImageViewTarget对象。那现在回到刚才into()方法的最后一行,可以看到,这里又将这个参数传入到了GenericRequestBuilder另一个接收Target对象的into()方法当中了。
- into()方法
public <Y extends Target<TranscodeType>> Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
//注意这一行
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
看这段代码 第二个if有一个isModelSet对象
那么他是用来做什么的呢。
其实这个对象也很好理解官方也给出了很明晰的注释
// model may occasionally be null, so to enforce that load() was called, set a boolean rather than relying on model
// not to be null.
private boolean isModelSet;
其实into方法最核心,最关键的是通过buildRequest()方法构建出了一个Request对象,还有requestTracker.runRequest(request);来执行这个Request。
那么Request对象是用来做什么的?
其实Request是用来发出加载图片请求的,它是Glide中非常关键的一个组件。我们先来看buildRequest()方法是如何构建Request对象的:
private Request buildRequest(Target<TranscodeType> target) {
if (priority == null) {
priority = Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}
private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
if (thumbnailRequestBuilder != null) {
if (isThumbnailBuilt) {
throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, "
+ "consider using clone() on the request(s) passed to thumbnail()");
}
// Recursive case: contains a potentially recursive thumbnail request builder.
if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
thumbnailRequestBuilder.animationFactory = animationFactory;
}
if (thumbnailRequestBuilder.priority == null) {
thumbnailRequestBuilder.priority = getThumbnailPriority();
}
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
thumbnailRequestBuilder.overrideHeight)) {
thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
}
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
// Guard against infinite recursion.
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
isThumbnailBuilt = false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
//注意这一行
// Base case: no thumbnail.
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
}
}
buildRequest()方法的内部其实又调用了buildRequestRecursive()方法,而buildRequestRecursive()方法中的代码虽然有点长,但是其中90%的代码都是在处理缩略图的。在我们的平时使用中,最主要的应该是最后一行
obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
这里调用了obtainRequest()方法来获取一个Request对象,而obtainRequest()方法中又去调用了GenericRequest的obtain()方法。注意这个obtain()方法需要传入非常多的参数,而其中很多的参数我们都是比较熟悉的,像什么placeholderId、errorPlaceholder、diskCacheStrategy等等。因此,我们就有理由猜测,刚才在load()方法中调用的所有API,其实都是在这里组装到Request对象当中的。
private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderId,
errorPlaceholder,
errorId,
fallbackDrawable,
fallbackResource,
requestListener,
requestCoordinator,
glide.getEngine(),
transformation,
transcodeClass,
isCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
}
public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(
LoadProvider<A, T, Z, R> loadProvider,
A model,
Key signature,
Context context,
Priority priority,
Target<R> target,
float sizeMultiplier,
Drawable placeholderDrawable,
int placeholderResourceId,
Drawable errorDrawable,
int errorResourceId,
Drawable fallbackDrawable,
int fallbackResourceId,
RequestListener<? super A, R> requestListener,
RequestCoordinator requestCoordinator,
Engine engine,
Transformation<Z> transformation,
Class<R> transcodeClass,
boolean isMemoryCacheable,
GlideAnimationFactory<R> animationFactory,
int overrideWidth,
int overrideHeight,
DiskCacheStrategy diskCacheStrategy) {
@SuppressWarnings("unchecked")
GenericRequest<A, T, Z, R> request = (GenericRequest<A, T, Z, R>) REQUEST_POOL.poll();
if (request == null) {
request = new GenericRequest<A, T, Z, R>();
}
request.init(loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderResourceId,
errorDrawable,
errorResourceId,
fallbackDrawable,
fallbackResourceId,
requestListener,
requestCoordinator,
engine,
transformation,
transcodeClass,
isMemoryCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
return request;
}
可以看到,这里其实就是去new了一个GenericRequest对象,并在最后一行返回,也就是说,obtain()方法实际上获得的就是一个GenericRequest对象。另外这里又在第35行调用了GenericRequest的init(),里面主要就是一些赋值的代码,将传入的这些参数赋值到GenericRequest的成员变量当中,我们就不再跟进去看了。
好,那现在解决了构建Request对象的问题,接下来我们看一下这个Request对象又是怎么执行的。回到刚才的into()方法,你会发现在第18行调用了requestTracker.runRequest()方法来去执行这个Request,那么我们跟进去瞧一瞧,如下所示:
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
这里有一个简单的逻辑判断,就是先判断Glide当前是不是处理暂停状态,如果不是暂停状态就调用Request的begin()方法来执行Request,否则的话就先将Request添加到待执行队列里面,等暂停状态解除了之后再执行。
我们重点来看这个begin()方法。由于当前的Request对象是一个GenericRequest,因此这里就需要看GenericRequest中的begin()方法了,如下所示:
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
//加载错误图片 setErrorPlaceholder
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
//是否调用了override()设置了宽高
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
这里我们来注意几个细节,首先如果model等于null,model也就是我们在第二步load()方法中传入的图片URL地址,这个时候会调用onException()方法。如果你跟到onException()方法里面去看看,你会发现它最终会调用到一个setErrorPlaceholder()当中,如下所示:
@Override
public void onException(Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "load failed", e);
}
status = Status.FAILED;
//TODO: what if this is a thumbnail request?
if (requestListener == null || !requestListener.onException(e, model, target, isFirstReadyResource())) {
setErrorPlaceholder(e);
}
}
private void setErrorPlaceholder(Exception e) {
if (!canNotifyStatusChanged()) {
return;
}
Drawable error = model == null ? getFallbackDrawable() : null;
if (error == null) {
error = getErrorDrawable();
}
if (error == null) {
error = getPlaceholderDrawable();
}
target.onLoadFailed(e, error);
}
这个方法中会先去获取一个error的占位图,如果获取不到的话会再去获取一个loading占位图,然后调用target.onLoadFailed()方法并将占位图传入。
很简单,其实就是将这张error占位图显示到ImageView上而已,因为现在出现了异常,没办法展示正常的图片了。而如果你仔细看下刚才begin()方法,你会发现它又调用了一个target.onLoadStarted()方法,并传入了一个loading占位图,在也就说,在图片请求开始之前,会先使用这张占位图代替最终的图片显示。这也是我们在上一篇文章中学过的placeholder()和error()这两个占位图API底层的实现原理。
好,那么我们继续回到begin()方法。刚才讲了占位图的实现,那么具体的图片加载又是从哪里开始的呢?是在begin()方法的第10行和第12行。这里要分两种情况,一种是你使用了override() API为图片指定了一个固定的宽高,一种是没有指定。如果指定了的话,就会执行第10行代码,调用onSizeReady()方法。如果没指定的话,就会执行第12行代码,调用target.getSize()方法。这个target.getSize()方法的内部会根据ImageView的layout_width和layout_height值做一系列的计算,来算出图片应该的宽高。不管是哪种情况,最终都会调用到onSizeReady()方法,在这里进行下一步操作。所以来看看onSizeReady方法到底做了什么:
/**
* A callback method that should never be invoked directly.
*/
@Override
public void onSizeReady(int width, int height) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
//glide代码恶心就恶心在这里,看到这一行的时候有个疑问,loadProvider是什么时候创建的???loadProvider是个啥?
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
从这里开始,真正复杂的地方来了,我们需要慢慢进行分析。看完这一段代码,脑子里的想法就是:
loadProvider是什么?ModelLoader是什么?DateFetcher和ResourceTranscoder又是什么?
首先,要搞清楚这点,需要先回到第二步的load()方法当中。还记得load()方法是返回一个DrawableTypeRequest对象吗?刚才我们只是分析了DrawableTypeRequest当中的asBitmap()和asGif()方法,并没有仔细看它的构造函数,现在我们重新来看一下DrawableTypeRequest类的构造函数:
private static <A, Z, R> FixedLoadProvider<A, ImageVideoWrapper, Z, R> buildProvider(Glide glide,
ModelLoader<A, InputStream> streamModelLoader,
ModelLoader<A, ParcelFileDescriptor> fileDescriptorModelLoader, Class<Z> resourceClass,
Class<R> transcodedClass,
ResourceTranscoder<Z, R> transcoder) {
if (streamModelLoader == null && fileDescriptorModelLoader == null) {
return null;
}
if (transcoder == null) {
//用于对图片进行转码的,由于ResourceTranscoder是一个接口,这里实际会构建出一个GifBitmapWrapperDrawableTranscoder对象。
transcoder = glide.buildTranscoder(resourceClass, transcodedClass);
}
//它是用于对图片进行编解码的,由于DataLoadProvider是一个接口,这里实际会构建出一个ImageVideoGifDrawableLoadProvider对象。
DataLoadProvider<ImageVideoWrapper, Z> dataLoadProvider = glide.buildDataProvider(ImageVideoWrapper.class,
resourceClass);
ImageVideoModelLoader<A> modelLoader = new ImageVideoModelLoader<A>(streamModelLoader,
fileDescriptorModelLoader);
//new出一个FixedLoadProvider,并把刚才构建的出来的GifBitmapWrapperDrawableTranscoder、ImageVideoModelLoader、ImageVideoGifDrawableLoadProvider都封装进去,这个也就是onSizeReady()方法中的loadProvider了。
return new FixedLoadProvider<A, ImageVideoWrapper, Z, R>(modelLoader, transcoder, dataLoadProvider);
}
DrawableTypeRequest(Class<ModelType> modelClass, ModelLoader<ModelType, InputStream> streamModelLoader,
ModelLoader<ModelType, ParcelFileDescriptor> fileDescriptorModelLoader, Context context, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle, RequestManager.OptionsApplier optionsApplier) {
super(context, modelClass,
buildProvider(glide, streamModelLoader, fileDescriptorModelLoader, GifBitmapWrapper.class,
GlideDrawable.class, null),
glide, requestTracker, lifecycle);
this.streamModelLoader = streamModelLoader;
this.fileDescriptorModelLoader = fileDescriptorModelLoader;
this.optionsApplier = optionsApplier;
}
可以看到构造函数中,调用了一个buildProvider()方法,并把streamModelLoader和fileDescriptorModelLoader等参数传入到这个方法中,这两个ModelLoader就是之前在loadGeneric()方法中构建出来的。
调用了glide.buildTranscoder()方法来构建一个ResourceTranscoder,它是用于对图片进行转码的,由于ResourceTranscoder是一个接口,这里实际会构建出一个GifBitmapWrapperDrawableTranscoder对象。
接下来调用了glide.buildDataProvider()方法来构建一个DataLoadProvider,它是用于对图片进行编解码的,由于DataLoadProvider是一个接口,这里实际会构建出一个ImageVideoGifDrawableLoadProvider对象。
接下来new了一个ImageVideoModelLoader的实例,并把之前loadGeneric()方法中构建的两个ModelLoader封装到了ImageVideoModelLoader当中。
最后new出一个FixedLoadProvider,并把刚才构建的出来的GifBitmapWrapperDrawableTranscoder、ImageVideoModelLoader、ImageVideoGifDrawableLoadProvider都封装进去,这个也就是onSizeReady()方法中的loadProvider了。
那么我们回到onSizeReady()方法中,在onSizeReady()方法分别调用了loadProvider的getModelLoader()方法和getTranscoder()方法,那么得到的对象也就是刚才我们分析的ImageVideoModelLoader和GifBitmapWrapperDrawableTranscoder了。又调用了ImageVideoModelLoader的getResourceFetcher()方法,这里我们又需要跟进去瞧一瞧了,
@Override
public DataFetcher<ImageVideoWrapper> getResourceFetcher(A model, int width, int height) {
DataFetcher<InputStream> streamFetcher = null;
if (streamLoader != null) {
//loadGeneric()方法中构建出的HttpUrlGlideUrlLoader,调用它的getResourceFetcher()方法会得到一个HttpUrlFetcher对象
streamFetcher = streamLoader.getResourceFetcher(model, width, height);
}
DataFetcher<ParcelFileDescriptor> fileDescriptorFetcher = null;
if (fileDescriptorLoader != null) {
fileDescriptorFetcher = fileDescriptorLoader.getResourceFetcher(model, width, height);
}
if (streamFetcher != null || fileDescriptorFetcher != null) {
//new出了一个ImageVideoFetcher对象,并把获得的HttpUrlFetcher对象传进去。
return new ImageVideoFetcher(streamFetcher, fileDescriptorFetcher);
} else {
return null;
}
}
可以看到,会先调用streamLoader.getResourceFetcher()方法获取一个DataFetcher,而这个streamLoader其实就是我们在loadGeneric()方法中构建出的HttpUrlGlideUrlLoader(StreamStringLoader郭霖写的是在这里获取),调用它的getResourceFetcher()方法会得到一个HttpUrlFetcher对象。然后new出了一个ImageVideoFetcher对象,并把获得的HttpUrlFetcher对象传进去。也就是说,ImageVideoModelLoader的getResourceFetcher()方法得到的是一个ImageVideoFetcher。
那么我们再次回到onSizeReady()方法,在onSizeReady()方中,这里将刚才获得的ImageVideoFetcher、GifBitmapWrapperDrawableTranscoder等等一系列的值一起传入到了Engine的load()方法当中。接下来我们就要看一看,这个Engine的load()方法当中,到底做了什么?代码如下所示:
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
//从缓存中读取对应的资源文件(LruCache)
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
//从可用弱引用中寻找缓存 isMemoryCacheable就是是否跳过内存缓存
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
//前面大多都是处理缓存逻辑 从这里开始看
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
load逻辑
EngineJob干嘛的?那EngineRunnable,DecodeJob呢?
调度DecodeJob,添加,移除资源回调,并notify回调(实现了一个比较重要的接口MainThreadCallback用于Engine任务完成时回调主线程)
DecodeJob调度任务的核心类,整个请求的繁重工作都在这里完成:处理来自缓存或者原始的资源,应用转换动画以及transcode。
Glide缓存机制来啦
这里穿插一下 讲一下glide的缓存机制
Glide是使用了LruCache做缓存机制,当然你可以不喜欢你可以自己实现自定义的缓存机制,glide是支持自定义缓存的。
两级内存缓存:硬盘缓存和内纯缓存。(硬盘缓存和内存缓存都是使用了LruCache,只是硬盘缓存使用了自定义的DiskLruCache基本没差)
先从LruCache中寻找,如果找到了缓存,将图片移出LruCache,加入activeResources弱引用缓存。如果在LruCache中没找到的话到activeResources弱引用缓存中寻找。如果在内存缓存中找到,则引用计数加1。使用中的图片用弱引用缓存来管理,没有使用的图片用LruCache来管理,判断图片有没有使用的依据之一是引用计数,当引用计数等于0时,将图片从弱引用缓存中移走,加入LruCache中。(acquire()和release()方法)
//缓存中
private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> cached = getEngineResourceFromCache(key);
if (cached != null) {
//注意这个方法 如果不为空说明缓存中有我们需要的资源,则将他加入到弱引用中,并调用acquire方法 ++
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
return cached;
}
@SuppressWarnings("unchecked")
private EngineResource<?> getEngineResourceFromCache(Key key) {
Resource<?> cached = cache.remove(key);
final EngineResource result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
// Save an object allocation if we've cached an EngineResource (the typical case).
result = (EngineResource) cached;
} else {
result = new EngineResource(cached, true /*isCacheable*/);
}
return result;
}
//弱引用
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource<?> active = null;
WeakReference<EngineResource<?>> activeRef = activeResources.get(key);
if (activeRef != null) {
active = activeRef.get();
if (active != null) {
//注意这个方法
active.acquire();
} else {
activeResources.remove(key);
}
}
return active;
}
void acquire() {
if (isRecycled) {
throw new IllegalStateException("Cannot acquire a recycled resource");
}
if (!Looper.getMainLooper().equals(Looper.myLooper())) {
throw new IllegalThreadStateException("Must call acquire on the main thread");
}
++acquired;
}
void release() {
if (acquired <= 0) {
throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
}
if (!Looper.getMainLooper().equals(Looper.myLooper())) {
throw new IllegalThreadStateException("Must call release on the main thread");
}
if (--acquired == 0) {
listener.onResourceReleased(key, this);
}
}
我们已经搞明白了内存缓存读取的原理,接下来的问题就是内存缓存是在哪里写入的呢?这里我们又要回顾一下上一篇文章中的内容了。还记不记得我们之前分析过,当图片加载完成之后,会在EngineJob当中通过Handler发送一条消息将执行逻辑切回到主线程当中,从而执行handleResultOnMainThread()方法。那么我们现在重新来看一下这个方法,代码如下所示:
private void handleResultOnMainThread() {
if (isCancelled) {
resource.recycle();
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
// synchronously released by one of the callbacks.
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
}
public void onEngineJobComplete(Key key, EngineResource<?> resource) {
Util.assertMainThread();
// A null resource indicates that the load failed, usually due to an exception.
if (resource != null) {
resource.setResourceListener(key, this);
if (resource.isCacheable()) {
activeResources.put(key, new ResourceWeakReference(key, resource, getReferenceQueue()));
}
}
// TODO: should this check that the engine job is still current?
jobs.remove(key);
}
再提一点 在Glide当中我们所说的资源其实是分两种
- 原图(SOURCE) :原始图片
- 处理图(RESULT) :经过压缩和变形等处理后的图片
缓存结束。
网友评论