Glide的源码比较复杂,功能也比较多,我看这个框架的时候,结合一位大佬写的博客,看了一个多月,才马虎看明白Glide的缓存原理
1.我们都知道Glide是一个很优秀的框架,用起来非常简单,功能强大,越是用起来简单的东西,源码就越复杂,下边我们来扒一扒它的源码
我们使用的时候,直接使用
Glide.with(Context).load(url).into(imageview)
我们直入主题,核心代码就在Engine这个类的load()方法中
public synchronized <R> LoadStatus load(){
...
// 生成图片的唯一key,通过key获取到图片
EngineKey key = keyFactory.buildKey();
// 这是第一个缓存
EngineResource<?> active = loadFromActiveResources(key,isMemoryCacheable);
if(active != null) {
cb.onResourceReady(active,DataSource.MEMORY_CACHE);
}
// 第二个缓存
EngineResource<?> cached = loadFromCache(key,isMoryCacheable);
if(cached != null) {
cd.onResouceReady();
}
// 第三个缓存,查看正在请求的任务里面是否有当前需要的图片,如果有,则增加一个回调
EngineJob current = jobs.get(key,onluyRetrieveFromCache);
if(current != null) {
current.addCallback(cb,callbackExecutor);
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,priotiry,diskCacheStrategy,transformations,...,engineJob);
jobs.put(key,engineJob);
engineJob.addCallback(cb,callbackExecutor);
engineJob.start(decodeJob);
return new LoadStatus(cb,engineJob);
}
/***
ActiveResources是第一级缓存,表示当前正在使用的资源,而且这个缓存没有大小限制,通过源码可以明白,里面使用了WeakReference弱引用,里面是一个hashMap,key就是图片的key,value则是WeakReference引用的EngineResource对象。
这里需要注意的是,除了WeakReference之外,还有一个引用队列,我们仔细思考一个问题,因为使用的是WeakReference,所以一旦发生GC那么图片很可能被回收,而我们的ActiveResources这个类是怎么监控到图片被回收的呢?
通过ReferenceQueue,引用队列来监控到的,那么ReferenceQueue和WeakReference是怎么关联起来的呢?
通过ResourceWeakReference这个类将WeakReference和ReferenceQueue关联起来的。
ReferenceQueue大概就是用来追踪WeakReference是否被回收的
*/
private EngineResource loadFromActiveResources(Key key,boolean isMemoryCacheable){
if(!isMemoryCacheable){
return null;
}
EngineResource active = activeResources.get(key);
if(active != null){
active.acquire();
}
return active;
}
这个里面就是从LruResourceCache中获取到缓存,但是这个中间为什么要判断是不是EngineResource呢?难道还存其他的类型?
private EngineResource getEngineResourceFromCache(Key key){
// 从LruResourceCache中获取到value,其实也就是用LruCache(LinkedHashMap)
Resource cached = cache.remove(key);
final EngineResource result;
if(cached == null) {
result = null
} else if(cache instance EngineResource){
result = cache;
} else{
result = new EngineResource(cached,true,true)
}
return result;
}
第三级则是从加载任务中查找
EngineJob current = jobs.get(key,onlyRetrieveFromCache);
if(current != null){
current.addCallback(cb,cabllbackExecutor);
return new LoadStatus(cb,current);
}
最后就是从源文件中获取了,这个源文件指的是网络或者硬盘原图缓存或者硬盘裁剪之后的缓存文件中获取,我们可以跟踪文件
DecodeJob decodeJob = decodeJobFactory.build();
jobs.put(key,engineJob);
engineJob.addCallback(cb,callbackExecutor);
// 这个start()才是开启线程运行
engineJob.start(decodeJob);
我们跟踪到DecodeJob类的run()方法里面
DecodeJob类
public void run(){
...
runWrapped();
...
}
private void runWrapped(){
runWrappedCount++;
switch(runReason){
case INITTIALIZE:
runGenerators();
break
...
}
}
private void runGenerators(){
currentThread = Thread.currentThread();
boolean isStarted = false;
while(!isCancelled && currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
.....
}
}
重点在上面的currentGenerator.startNext方法中,我们跟踪到SourceGenerator类里面
class SourceGenerator implements DataFetcherGenerator,DataFetcher.DataCallback<Object>,DataFetcherGenerator.FetcherReadyCallback{
public boolean startNext(){
...
// 这里才是真正加载源文件的地方,这个类有欧很多个实现类,比如说HttpUrlFetcher,AssetPathFetcher,FilePathFetcher等等,
loadData.fetcher.loadData();
...
}
}
我们看一个最经常用的,HttpUrlFetcher
public void loadData(){
...
InputStram result = loadDataWithRedirects();
...
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map<String, String> headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
这才算是找到根源上了,其实从网络获取完成之后,会先保存到磁盘里,然后再从磁盘中获取图片去显示,并且会放到ActiveResource类里面