前文《Android图片加载问题分析》中提到3.0及以上的Android系统上,BitmapFactory创建Bitmap时可以复用之前创建的Bitmap,特别是在5.0及以上时复用的Bitmap不再要求和新Bitmap的像素数据一样大。
但是运行时哪些Bitmap才能被拿来复用呢?
下面将通过的Glide源码的分析,了解Glide是怎样做的。
理解RequestManager
Glide.with做了什么
Glide.with有5个重载方法,返回值都是RequestManager
,参数分别为
- android.content.Context
- android.app.Activity
- android.app.Fragment
- android.support.v4.app.Fragment
- android.support.v4.app.FragmentActivity
这些with方法最终都会通过以下三个方法创建RequestManager
。
private RequestManager getApplicationManager(Context context) {
MediaScannerConnection.scanFile(null, null, null,null);
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
Glide glide = Glide.get(context);
applicationManager =
new RequestManager(
glide, new ApplicationLifecycle(), new EmptyRequestManagerTreeNode());
}
}
}
return applicationManager;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm,
android.app.Fragment parentHint) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager =
new RequestManager(glide, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
RequestManager supportFragmentGet(Context context, FragmentManager fm, Fragment parentHint) {
SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
Glide glide = Glide.get(context);
requestManager =
new RequestManager(glide, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
-
getApplicationManager
获得的是一个单例的对象,表示全局只有一个applicationManager
。 - 后两个是将
RequestManager
与SupportRequestManagerFragment
或RequestManagerFragment
绑定 - 这两种Fragment又是通过
FragmentManager fm
和Fragment parentHint
获取的 - RequestManager创建时,使用了
RequestManagerFragment#current.getLifecycle
以上三个方法都是被RequestManagerRetriever#get
调用的。在RequestManagerRetriever#get
的几个重载方法中能看到:
- 在后台线程中获取的
RequestManager
都是applicationManager
,这是因为后台线程可能比页面存活的时间更长。 - 在3.0及以下的系统上,通过Actitivy获取的
RequestManager
也是applicationManager
,这是因为老的Activity中还不能添加Fragment。
RequestManagerFragment是什么
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(
final android.app.FragmentManager fm, android.app.Fragment parentHint) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
-
RequestManagerFragment
是一个Fragment
,并被FragmentManager
添加到特定的页面了(可能是Activity或Fragment) - 同一个页面只会存在一个RequestManagerFragment与之对应
看到这里,我们自然会想到,RequestManager
通过RequestManagerFragment
获得了所在页面的生命周期。在页面onDestroy
时,RequestManager#onDestroy
也会被调用。这里不再贴这部分的详细代码了。只看看RequestManager#onDestroy
做了什么,其中的clear(target)
将是下一节中释放过程的入口。
/**
* Lifecycle callback that cancels all in progress requests and clears and recycles resources for
* all completed requests.
*/
@Override
public void onDestroy() {
targetTracker.onDestroy();
for (Target> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
Glide中的缓存管理和Bitmap复用
通过Glide的源码,我们很容易发现一个进程中只会存在一个Engine
实例。我们这里主要关注Engine中的三个变量
Map
>> activeResources MemoryCache cache
LazyDiskCacheProvider diskCacheProvider
MemoryCache很好理解,就是我们常说的内存缓存。虽然Glide的源码通过LruResourceCache实现MemoryCache,而LruResourceCache通过泛型使其可以缓存各种对象,但这里仅以BitmapResource作为讨论对象,其缓存的就是Bitmap实例。
diskCacheProvider就是对磁盘缓存的封装,Engine不管理磁盘缓存,而是在DecodeJob中使用磁盘缓存。DecodeJob会根据请求的特点,决定存储的转换后的图片,还是原图片。图片的下载也是在DecodeJob中通过DataFetcher处理。
decodeJobFactory = new DecodeJobFactory(diskCacheProvider);
activeResources是Glide通过引用计数实现Bitmap复用的关键
Key是通过每次load的参数生成的
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
Value是EngineResource的弱引用。EngineResource中和Bitmap复用相关的主要有三个方法acquire,release和recyle,正是通过对EngineResource的引用计数,才能找到那些不会系统使用的Bitmap。
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);
}
}
@Override
public void recycle() {
if (acquired > 0) {
throw new IllegalStateException("Cannot recycle a resource while it is still acquired");
}
if (isRecycled) {
throw new IllegalStateException("Cannot recycle a resource that has already been recycled");
}
isRecycled = true;
resource.recycle();
}
而BitmapResource#recycle
则将bitmap放回回收池bitmapPool.put(bitmap);
加载过程
可以看到每个成功执行的加载任务都会给调用一次EngineResource#acquire
,从而增加引用计数。
释放过程
释放过程的逻辑前文已经基本说清楚了,这里列出完整的调用栈
- AcitivityFragmentLifecycle#onDestory
- RequestManager#onDestroy -> clear -> untrackOrDelegate
- Glide#removeFromManagers
- RequestManager#untrack
- RequestTracker#clearRemoveAndRecycle
- SingleRequest#clear releaseResource
- Engine#release
- EngineResource#release:当引用计数为0时,触发
listener.onResourceReleased(key, this)
- ResourceListener#onResourceReleased
- Engine#onResourceReleased:如果Recourse能被缓存,则加入MemeoryCache,否则释放资源。
- ResourceRecycler#recycle
- BitmapResource#recycle:(将Bitmap放入回收池)
//Engine
@Override
public void onResourceReleased(Key cacheKey, EngineResource resource) {
Util.assertMainThread();
activeResources.remove(cacheKey);
if (resource.isCacheable()) {
cache.put(cacheKey, resource);
} else {
resourceRecycler.recycle(resource);
}
}
除了applicationManager管理的请求,所有的请求都会随着页面的Destroy调用一次EngineResource#release
,从而能确保被回收的资源不被任何页面使用。这里看到放入MemoryCache的资源,都是页面不再使用的资源。页面从MemoryCache获取缓存的同时,会将Resouce从MemoryCache中删除。
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 /*isMemoryCacheable*/);
}
return result;
}
因此可以确保从MemoryCache中移除的Bitmap能被放到BitmapPool中安全的复用。
Glide中Bitmap的流动
最后通过一张图片,了解Glide中Bitmap的流动
Glide的假三级缓存和Fresco的真三级缓存
Fresco的三级缓存在上篇文章中介绍过,如下图,它增加了未解码图片的内存缓存,减少文件IO。
从前文的加载过程可以看到Glide从缓存获取Bitmap的来源也有三个,activeResources,MemoryCache和DiskCache。看上去很像是三级缓存,但实际上activeResources和MemoryCache是互补的。
- activeResources存储页面中使用的Bitmap的弱引用
- MemoryCache存储页面不再使用的Bitmap
所以activeResources从严格意义上看来不能算是缓存。它存在的最主要的意义是,通过弱引用保存页面使用的Bitmap。通过activeResources和MemoryCache一起,就能精确的了解整个应用中所有通过Glide创建的Bitmap的状态。