我们最常用的gilde加载图片的时候就用短短的方法就将图片展示出来了:
Glide.with(this).load(url).into(imageview);
在这简单的操作后面是很多的代码,先从这一个个方法开始看起。
1.with()
RequestManager with()
public class Glide{
·····
//适用于在正常fragment之外使用的资源或activity生命周期(在服务中或者通知缩略图)
//@param context 任何上下文都不会被保留
//@return 可用于启动加载的顶级应用程序的RequestManager
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
//该加载与传入的activity生命周期相关联
//
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
}
- with()方法重载的中类很多,可以传入context、activity、fragment等
- RequsetManager调用get方法获得RequestManagerRetriever对象
- RequestManagerRetriever调用get方法获取RequestManager对象
Glide#getRetriever
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
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();
}
调用Glide.get方法,初始化glide
@NonNull
public 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;
}
Glide加载是个静态方法,且采用单例模式
RequestManagerRetriever#get()
public class RequestManagerRetriever implements Handler.Callback {
```
private volatile RequestManager applicationManager;
@NonNull
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or
// activity. However, in this case since the manager attached to the application will not
// receive lifecycle events, we must force the manager to start resumed using
// ApplicationLifecycle.
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
@NonNull
public 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);
}
@NonNull
public 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));
}
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
Preconditions.checkNotNull(
fragment.getContext(),
"You cannot start a load on a fragment before it is attached or after it is destroyed");
if (Util.isOnBackgroundThread()) {
return get(fragment.getContext().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
}
}
@SuppressWarnings("deprecation")
@NonNull
public RequestManager get(@NonNull Activity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
}
}
······
}
这里的get方法是根据传入的不同的参数制定的。实际上只分为两种参数Application类型的参数和传入非Application类型的参数。
- 传入Application参数类型的情况:
在Glide.with()方法中传入的是一个Application对象,就会调用get()方法中参数为context的方法。然后会调用getApplicationManager()方法来获取一个RequestManager对象。
这是最简单的一种情况。因为Application对象的生命周期就是应用程序的生命周期。Glide的生命周期和应用程序的生命周期就是同步的。关闭应用程序,Glide的加载也会终止。
- 传入非Application参数类型的情况:
这种情况下会像当前传入的Activity等中添加一个隐藏的fragment。比如说Glide正在加载一张图片如果此时关闭了当前应用,那么glide应当停止对当前图片的加载,但是glide并不知道当前activity的生命周期,但是fragment的生命周期和activity是同步的,若activity被销毁了,fragment是可以监听到的,因此添加一个隐藏的fragment。
RequestManagerRetriever#fragmentGet
添加隐藏的fragment在supportFragmentGet()和fragmentGet()方法中。这两个方法中有一个类RequestManagerFragment继承于fragment,创建了fragment对象。
private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
//创建fragment对象
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
RequestManagerFragment
public class RequestManagerFragment extends Fragment {
······
public RequestManagerFragment() {
this(new ActivityFragmentLifecycle());
}
······
}
这里面定义了一个ActivityFragmentLifecycle类来监听fragment生命周期的变化
ActivityFragmentLifecycle
LifecycleListener监听fragment生命周期
class ActivityFragmentLifecycle implements Lifecycle {
@Override
public void addListener(@NonNull LifecycleListener listener) {
lifecycleListeners.add(listener);
if (isDestroyed) {
listener.onDestroy();
} else if (isStarted) {
listener.onStart();
} else {
listener.onStop();
}
}
@Override
public void removeListener(@NonNull LifecycleListener listener) {
lifecycleListeners.remove(listener);
}
void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}
void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
}
2. load()
RequestManager
因为with()方法返回的是RequestManager对象,所以调用load方法的类肯定是RequestManager。load方法可加载的类型有很多,这里只拿加载图片的url为例。
我看的这一版的gilde已经是3.10.0的了。网上一些博客中的DrawTypeRequest已经没有了。
public class RequestManager
implements ComponentCallbacks2, LifecycleListener, ModelTypes>{
······
public RequestBuilder asDrawable() {
return as(Drawable.class);
}
public RequestBuilder load(@Nullable URL url) {
return asDrawable().load(url);
}
//as方法中传入的是Drawable对象
@NonNull
@CheckResult
public RequestBuilder asDrawable() {
return as(Drawable.class);
}
//resourceClass要解码的资源
//该方法返回一个新的请求构建器,用于加载给定的资源类,进入RequestBuilder
@NonNull
@CheckResult
public RequestBuilder as(
@NonNull Class resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
······
}
看到加载图片的load方法都返回RequestBuilder
上面的源码是从load()-->asDrawable()-->as()的过程,as方法中的resourceClass就是传进来的Drawable.class这里的drawable指代的是一种资源的类型。下面再看下RequestBuilder这个类。
RequestManager中的load方法主要就是通过实例化RequestBuilder,再调用RequestBuilder中的load方法。
RequestBuilder
public class RequestBuilder extends BaseRequestOptions>
implements Cloneable, ModelTypes> {
//构造方法,as中调用的方法就是这个
protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();
initRequestListeners(requestManager.getDefaultRequestListeners());
apply(requestManager.getDefaultRequestOptions());
}
······
//load加载的资源类型有很多种,这里还是只放了url的
public RequestBuilder load(@Nullable Uri uri) {
return loadGeneric(uri);
}
@NonNull
private RequestBuilder loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
}
在这个类可以看见一个自定义的泛型,TranscodeType转码类型,他代表可以传进来的url file bitmp等不同类型的资源。官方介绍是一个通用类,可以处理通用资源类型的设置选项和启动负载。
load方法又调用了loadGeneric方法,这里面设置了两个变量,mode和isModleSet。
into
从load.into的调用关系可以知道into方法在RequestBuilder类中
@NonNull
public > Y into(@NonNull Y target) {
return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
}
@NonNull
@Synthetic
> Y into(
@NonNull Y target,
@Nullable RequestListener targetListener,
Executor callbackExecutor) {
return into(target, targetListener, /*options=*/ this, callbackExecutor);
}
@NonNull
public ViewTarget into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
into方法最终会返回一个target对象。通过glide.buildImageViewTarget(view,transcodeClass)创建一个target对象,Target对象是最终用来展示图片的。
buildImageViewTarget
@NonNull
public ViewTarget buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
public ViewTarget buildTarget(
@NonNull ImageView view, @NonNull Class clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
创建target对象主要分两种:
- 加载图片时调用了asBitmap()方法,会构建BitmapImageViewTarget对象
- 其他情况,构建GlideDrawableImageviewTarget对象
GenericRequestBuilder#into(Target target)
private > Y into(
@NonNull Y target,
@Nullable RequestListener targetListener,
BaseRequestOptions> options,
Executor callbackExecutor) {
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
//Request加载资源的请求类 Target继承于LifecyclerListener,通过getRequest检索到此目标当前的请求
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
//跟踪、取消和重新启动进程中、已完成和失败请求
requestManager.track(target, request);
return target;
//3.7.0版本
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;
}
这里会检查是不是在主线程,因为更新ui的操作必须在主线程。这里通过target对象获取request对象,然后清除之前的request对象,回收request对象,然后重新构建一个request对象,并给这个target对象设置request对象。
下面的分析拿glide3.7.0的版本来看。现在4.10.0版本和之前的版本差别太大了。
在这之后就开始加载图片。buildRequest()方法构建出来Request对象,buildRequest-->buildRequestRecursive()-->obtainRequest-->GenericRequest.obtain()-->REQUEST_POOL.poll()。调用了请求池的poll方法,获取request对象。如果这个对象为空就创建一个GenericRequest对象,然后用request对象的init方法进行初始化。
runRequest
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
//执行request
request.begin();
} else {
//将request加入待执行队列
pendingRequests.add(request);
}
}
先判断Glide当前是不是处于暂停状态。
在执行图片的加载过程中有几个重要的类:
- ModeLoader:获取原始数据的类
- ResourceTranscoder:图片资源转换的类
- Engine:负责启动负载并管理活动和缓存的资源类
- DataLoadProvider:负责图片的编码和解码
- DataFetcher:将流资源转换为glide实际加载图片需要的数据,比如byte[] file uri url等
- EngineJob:开启线程,为异步加载图片做准备
缓存
glide缓存主要分为内存缓存和硬盘缓存。这里将从这两个方向来分析。
缓存key
缓存功能要实现的话肯定是要有进行缓存的key的。拿到这个key我们才能找到对应的图片。
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
public LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,
DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder 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());
...
}
...
}
这里调用fetcher.getId()方法获得了一个id字符串,这个字符串是加载图片的唯一标识。EngineKey就是保存key的类,一个key有10个标识可以区分。
内存缓存
默认情况下,Glide自动就是会开启内存缓存的。如果不想开启内存缓存,只需要设置skipMemoryCache(true)就可以了。
Glide.with(this)
.load(url)
.skipMemoryCache(true)
.into(imageView);
Glide缓存实现了LruCache算法,还结合了一种弱引用的机制,共同完成了内存缓存的功能。
Glide获取缓存资源对象
loadGeneric()-->Glide.buildStreamModelLoader()-->buildModelLoader()-->Glide.get()-->createGlide()
public class GlideBuilder {
...
Glide createGlide() {
if (sourceService == null) {
final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
sourceService = new FifoPriorityThreadPoolExecutor(cores);
}
if (diskCacheService == null) {
diskCacheService = new FifoPriorityThreadPoolExecutor(1);
}
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
if (bitmapPool == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int size = calculator.getBitmapPoolSize();
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (memoryCache == null) {
//Glide实现内存缓存使用的LruCache对象
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
engine = new Engine(memoryCache, diskCacheFactory, diskCacheService, sourceService);
}
if (decodeFormat == null) {
decodeFormat = DecodeFormat.DEFAULT;
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
}
LruCache算法的实现主要的点就在于它用了LindedHashMap来保存数据,其中的一个特性accessOrder = true是基于访问顺序的,再加上对LindkedHashMap的数据操作上锁实现的缓存策略。调用get方法访问缓存对象时,就会调用LinkedHashMap的get()方法获取对应集合的元素,同时会更新该元素到队列。当调用put()方法时,就会在集合中添加元素,并调用trimToSize()判断缓存是否以满,如果满了就用LinkedHashMap的迭代器删除对首元素,也就是近期最少访问的元素。
Engine#load()
内存缓存的代码在这里实现
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
...
public LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,
DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder 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());
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;
}
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 decodeJob = new DecodeJob(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);
}
...
}
在这个方法里其实可以把加载图片的过程分为三步:
- loadFromCache():从缓存中获取
- loadFromActivityResources():获取缓存图片
- engineJob.start(runnable):开启线程从网络加载图片
Glide加载过程中会调用loadFromCache()和loadFromActivityResources()这两个方法来获取内存缓存。一个使用的是LruCache算法,另一个使用的是弱引用。
loadFromCache/loadFromActivityResources
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
private final MemoryCache cache;
private final Map>> activeResources;
...
private EngineResource> loadFromCache(Key key, boolean isMemoryCacheable) {
if (!isMemoryCacheable) {
return null;
}
EngineResource> cached = getEngineResourceFromCache(key);
if (cached != null) {
cached.acquire();
activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
}
return cached;
}
private EngineResource> getEngineResourceFromCache(Key key) {
Resource> cached = cache.remove(key);
final EngineResource result;
if (cached == null) {
result = null;
} else if (cached instanceof EngineResource) {
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> activeRef = activeResources.get(key);
if (activeRef != null) {
active = activeRef.get();
if (active != null) {
active.acquire();
} else {
activeResources.remove(key);
}
}
return active;
}
...
}
loadFromCache()方法先会判断是否开启了内存缓存,开启了则会调用getEngineResourceFromCache()方法从cache对象也就是LruResourceCache中获取图片资源,但是却把这个资源从缓存中removele。获取到这个资源后把这个资源又加入到了activeResources中。这很令我困惑,为什么这样做,按理来说Lru会根据获取到的这个资源会把它重新加入到队列后面,为了就是会被删除。
activeResources就是一个弱引用的HashMap,用来缓存正在使用中的图片。loadFromActiveResources()方法就是从activeResources这个HashMap当中取值的。使用activeResources来缓存正在使用中的图片可以保护这些图片不会被LruCache算法回收掉。
内存缓存的写入
当图片加载完成后,会在EngineJob当中通过Handler发送一条消息将执行逻辑切回到主线程当中,从而执行了handleResultOnMainThread()方法。
class EngineJob implements EngineRunnable.EngineRunnableManager {
private final EngineResourceFactory engineResourceFactory;
...
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;
engineResource.acquire();
listener.onEngineJobComplete(key, engineResource);
for (ResourceCallback cb : cbs) {
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
cb.onResourceReady(engineResource);
}
}
engineResource.release();
}
static class EngineResourceFactory {
public EngineResource build(Resource resource, boolean isMemoryCacheable) {
return new EngineResource(resource, isMemoryCacheable);
}
}
...
}
EngineJob这个类就是主要就是来执行添加(addCallBack)和删除(rmoveCallBack)回调,并在回调时通知回调来管理负载的类加载完成。
handleResultOnMainThread()方法中通过EngineResourceFactory构建出了一个包含图片资源的EngineResource对象,将这个对象传入Engine的onEngineJobComplete方法中。
onEngineJobComplete
@Override
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);
}
可看到回调过来的EngineResource被put到了activeResources当中,在这里将图片写入了缓存。但这里只是弱引用缓存,还有一种LruCache在哪里呢?
acquire()和release()
class EngineResource implements Resource {
private int acquired;
...
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);
}
}
}
这两个方法handleResultOnMainThread()方法中出现过,engineResource调用了acquire()方法后,才执行了onEngineJobComplete()方法中,也就是将图片加入了activeResources弱引用集合中。将图片加入弱引用map达到将当前正在使用的图片加入的效果,就出现了acquire和release方法。
当acquired变量大于0时,说明图片正在使用,应该放入activeResources弱引用缓存当中。而经过release()之后,如果acquired值为0了,说明图片不再被使用了,将会调用listener的onResourceReleased()方法来释放资源。
onResourceReleased()
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
private final MemoryCache cache;
private final Map>> activeResources;
...
@Override
public void onResourceReleased(Key cacheKey, EngineResource resource) {
Util.assertMainThread();
activeResources.remove(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
...
}
这里首先会将缓存图片从activeresources中移除,然后再将它put到LruResourceCache中。否则就进行资源回收。这样就实现了正在使用中的图片使用弱引用来进行缓存,不在使用中的图片使用LruCache来进行缓存。
硬盘缓存
禁止硬盘缓存可以这样使用:
Glide.with(this)
.load(url)
.disCacheStrategy(DiskCacheStrategy.NONE)
.into(imageview);
disCacheStrategy可以接收四种参数:
- DiskCacehStrategy.NONE:不缓存任何内容
- DiskCacehStrategy.SOURCE:只缓存原始图片
- DiskCacehStrategy.RESULT:只缓存转换后的图片
- DiskCacehStrategy.ALL:既缓存原始图片,也缓存转换后的图片
decode()
Glide开启线程加载图片后会执行EngineRunnable的run()方法,run方法中又会调用一个decode方法
private Resource> decode() throws Exception {
if (isDecodingFromCache()) {
return decodeFromCache();
} else {
return decodeFromSource();
}
}
这里有两个方法:
- decodeFromCache():从硬盘缓存中读取图片
- decodeFromSource():读取原始图片
Glide会优先从硬盘缓存中读取
decodeFromCache
private Resource> decodeFromCache() throws Exception {
Resource> result = null;
try {
//DisCacheStrategy.RESULT
result = decodeJob.decodeResultFromCache();
} catch (Exception e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Exception decoding result from cache: " + e);
}
}
if (result == null) {
//DisCacheStrategy.SOURCE
result = decodeJob.decodeSourceFromCache();
}
return result;
}
执行两个方法的区别就在于传入disCacheStrategy方法中的参数
decodeResultFromCache
public Resource decodeResultFromCache() throws Exception {
if (!diskCacheStrategy.cacheResult()) {
return null;
}
long startTime = LogTime.getLogTime();
Resource transformed = loadFromCache(resultKey);
startTime = LogTime.getLogTime();
Resource result = transcode(transformed);
return result;
}
先调用loadFromCache()从缓存中读取数据,直接将数据解码并返回
decodeSourceFromCache()
public Resource decodeSourceFromCache() throws Exception {
if (!diskCacheStrategy.cacheSource()) {
return null;
}
long startTime = LogTime.getLogTime();
Resource decoded = loadFromCache(resultKey.getOriginalKey());
return transformEncodeAndTranscode(decoded);
}
先调用loadFromCache()从缓存中读取数据,调用transformEncodeAndTrancode()方法先将数据转换再解码返回。
这两个方法在调用loadFromCache()方法中传入的参数不同。因为Glide缓存的key是由10个参数共同组成的。如果是缓存原始图片,不需要那么多的参数。
硬盘缓存的读取
private Resource loadFromCache(Key key) throws IOException {
File cacheFile = diskCacheProvider.getDiskCache().get(key);
if (cacheFile == null) {
return null;
}
Resource result = null;
try {
result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);
} finally {
if (result == null) {
diskCacheProvider.getDiskCache().delete(key);
}
}
return result;
}
硬盘缓存的读取在loadFromCache()中。getDishCache()方法获取到Glide自己编写的DiskLruCache工具类的实例,然后调用它的get()方法把缓存key传入,就获得硬盘缓存的文件了。文件不为空,就将其解码成Resource对象后返回就行。
decodeFromSource()
public Resource decodeFromSource() throws Exception {
//解析原图片
Resource decoded = decodeSource();
//对图片进行转换和转码,再将转换过后的图片写入到硬盘缓存中
return transformEncodeAndTranscode(decoded);
}
在没有缓存的情况下,会调用decodeFromDecode()方法来读取原始图片。
decodeSource()
private Resource decodeSource() throws Exception {
Resource decoded = null;
try {
long startTime = LogTime.getLogTime();
//当相应的资源不在缓存中时才调用此方法,异步获取可以从中解码资源的数据
//fetcher是ImageVideoFetcher HttpUrlFetcher中真正进行网络请求
final A data = fetcher.loadData(priority);
if (isCancelled) {
return null;
}
//对数据进行解码
decoded = decodeFromSourceData(data);
} finally {
fetcher.cleanup();
}
return decoded;
}
decodeFromSourceData
private Resource decodeFromSourceData(A data) throws IOException {
final Resource decoded;
//是否允许缓存原始图片
if (diskCacheStrategy.cacheSource()) {
decoded = cacheAndDecodeSourceData(data);
} else {
long startTime = LogTime.getLogTime();
decoded = loadProvider.getSourceDecoder().decode(data, width, height);
}
return decoded;
}
private Resource cacheAndDecodeSourceData(A data) throws IOException {
long startTime = LogTime.getLogTime();
SourceWriter writer = new SourceWriter(loadProvider.getSourceEncoder(), data);
diskCacheProvider.getDiskCache().put(resultKey.getOriginalKey(), writer);
startTime = LogTime.getLogTime();
Resource result = loadFromCache(resultKey.getOriginalKey());
return result;
}
调用getDiskCache()方法来获取DiskLruCache实例,调用put方法写入硬盘缓存。原始图片的缓存key是用的resultKey.getOriginalKey()
参考文章
郭大神Glide系列源码分析