1.主要方法流程(4.9.0)
看下用法,主要是 with 、load和into三个主要的模块。
Glide.with(this)
.load(url)
.into(imgView);
- with方法
先来看with,是通过RequestManager这个方法来实现的,可以看到这几个重载的方法作用其实一样就是传入当前页面的上下文(Activity或者Fragment的)
RequestManager with(Context context)
RequestManager with(android.app.Activity)
RequestManager with(android.app.Fragment)
RequestManager with(android.support.v4.app.Fragment)
RequestManager with(android.support.v4.app.FragmentActivity)
重点是这个方法,无论在哪里调用,都会传入一个FragmentManager
这里用到了这个传入的fm,用来生成一个fragment,这个fragment,是绑定在当前Activity中的。
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
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;
}
可以看到RequestManager被创建,是需要一个FragmentManager的,其实他的作用是帮我们创建一个空的Fragment来实现对当前Activity页面的声明周期的监听,其实这也是with这个方法或者RequestManager主要功能,Glide通过在这个空Fragment里的生命周期监听回调,就可以感知到Activity的声明周期,这样就省了我们自己去维护了。比如之前用过的EventBus,在onCreate里register,在onDestroy里unregister,Gilde这里就全帮我们做了,省了我们自己去维护了。
- load方法
首先这个方法重载了一堆方法,不得不说Glide牛逼,都帮我们考虑到了,你能想到的加载图片的方式,一个load就都能传进去
Glide.with(this).load(R.drawable.ic_android).into(imageView);
Glide.with(this).load("http://xxx.xxx.png").into(imageView);
Glide.with(this).load(Uri.parse("xxxxx")).into(imageView);
Glide.with(this).load(getResources().getDrawable(R.drawable.ic_android)).into(imageView);
Glide.with(this).load(new File("xxx")).into(imageView);
Glide.with(this).load(BitmapFactory.decodeFile("xxx")).into(imageView);
然后会进步的根据我们传入类型不同,做进一步的处理
public class RequestManager implements LifecycleListener,
ModelTypes> {
······
@NonNull
@CheckResult
@Override
public RequestBuilder load(@Nullable String string) {
return asDrawable().load(string);
}
@NonNull
@CheckResult
public RequestBuilder asDrawable() {
return as(Drawable.class);
}
@NonNull
@CheckResult
public RequestBuilder as(
@NonNull Class resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
······
最后会返回这个RequestBuilder对象,然后在下一部进行处理。
- into方法
into方法一般都是传递了一个 ImageView 图片控件,然后Glide就会帮我们把之前传进去的资源给绘制到ImageView上,继上一个RequestBuilder对象
public class RequestBuilder implements Cloneable,
ModelTypes> {
@NonNull
public ViewTarget into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);//判断传进来的 ImageView 是否为空
RequestOptions requestOptions = this.requestOptions;
······
}
return into(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions);
}
}
当我们调用 RequestBuilder.into 方法时会根据传入参数创建对应类型的 Target 实现类。这里我们不考虑bitmap或者drawble,只看网络相关的,默认创建的Request对象是SingleRequest,由于默认是第一次加载图片,所以我们来看RequestManager的track方法。
void track(@NonNull Target> target, @NonNull Request request) {
...
requestTracker.runRequest(request);
}
//RequestTracker中
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {//当没有暂停时Request就开始执行
request.begin();
} else {//暂停执行
request.clear();
...
pendingRequests.add(request);
}
}
由于这里Request的具体实现是SingleRequest,所以我们来看它的begin方法。
public void begin() {
...
//传入的model为null,在本文中就是传入的URL为null
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
...
//图片加载失败
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
//从缓存中拿数据
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
//根据View的宽高来计算出图片的宽高,最后回调的也是onSizeReady方法
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
//图片开始加载时的默认显示
target.onLoadStarted(getPlaceholderDrawable());
}
...
}
}
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
...
loadStatus = engine.load(
glideContext,
model,
requestOptions.getSignature(),
this.width,
this.height,
requestOptions.getResourceClass(),
transcodeClass,
priority,
requestOptions.getDiskCacheStrategy(),
requestOptions.getTransformations(),
requestOptions.isTransformationRequired(),
requestOptions.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this);
...
}
}
主要看一下 engine.load
这个方法,这里边是Glide一些缓存的核心方法
public LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class> resourceClass,
Class transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map, Transformation>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb) {
...
//根据model计算出每张图片对应的key
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
//从ActiveResources中获取图片,ActiveResources是一个弱引用的HashMap
EngineResource> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
//从缓存中获取图片,采用了Lrucache
EngineResource> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
EngineJob> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
return new LoadStatus(cb, current);
}
EngineJob engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
//从本地或者网络获取数据,会切换到子线程
engineJob.start(decodeJob);
return new LoadStatus(cb, engineJob);
}
engineJob.start(decodeJob)这个方法就开始进行线程切换和触发网络请求了
public void start(DecodeJob decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
//向子线程添加任务
executor.execute(decodeJob);
}
2.缓存
主要是三级的一个缓存,ActiveResources缓存(内存)-> MemoryCache缓存(内存)->硬盘->都没有发起网络请求
看下上边那个engine.load里的方法
//从ActiveResources中获取图片,ActiveResources是一个弱引用的HashMap
EngineResource> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
return null;
}
//从缓存中获取图片,采用了Lrucache
EngineResource> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
return null;
}
正常先从网络加载到数据,然后放入磁盘缓存中,然后ActiveResources中可以引用到,计数+1,当ActiveResources中不再用到的时候,此时从自身移除(先不考虑GC),然后会放入到MemoryCache的中,而当MemoryCache也存储满了的时候(LRU算法),就会从自身移除掉,当这个图片再需用使用的时候,又会从磁盘开始重复上边的流程了。
需要注意的是在活动缓存里去取,如果取不到才去MemoryCache内存缓存中去,这两个存储是互斥的,如果在MemoryCache找到了,就会把MemoryCache中的删掉,放入到ActiveResources中去。
为什么有MemoryCache内存缓存,还需要弱引用的活动缓存呢?个人理解是MemoryCache 缓存是LRU算法的,里边会有一个maxsize,如果是最少使用,内部算法就会去回收了他,而当我们如果大量图片频繁滑动使用的时候,这时候可能会导致回收加快。ActiveResources活动内存是用弱引用,如果当前内存足够,理论上是会比MemoryCache缓存更多的资源,避免重复回收,同时当如果内存真的超出触发GC时候,那些没有被使用的资源也会通过GC被动回收。