我觉得内存管理是三方图库最重要的点, 而且该知识点能够应用到项目里, 所以着重看了一下fresco是如何回收内存的。
fresco内存释放分为2种方式:
1、按照LruCach的方式释放引用计数为0对象, fresco内部逻辑实现;
2、应用退到后台、手机低内存等场景下主动释放fresco的内存, 包括引用计数不为0的对象, 需要传事件给fresco。 参考: https://github.com/facebook/fresco/issues/1457
ImagePipeline imagePipeline = Fresco.getImagePipeline();
imagePipeline.clearMemoryCaches(); //内存,包括已解码和未解码
imagePipeline.clearDiskCaches(); //删除文件
// combines above two lines
imagePipeline.clearCaches();
上图是fresco官方提供的架构图, 按照三级缓存。 即已解码的图片,未解码的图片和文件缓存。Android5.0以前bitmap是缓存在ashmem里, Android5.0及以上保存在java堆里。 为了释放bitmap, fresco定义了引用计数类SharedPrefrence。
public class SharedReference<T> {
// Keeps references to all live objects so finalization of those Objects always happens after
// SharedReference first disposes of it. Note, this does not prevent CloseableReference's from
// being finalized when the reference is no longer reachable.
@GuardedBy("itself")
private static final Map<Object, Integer> sLiveObjects = new IdentityHashMap<>();
@GuardedBy("this")
private T mValue;
@GuardedBy("this")
private int mRefCount;
private final ResourceReleaser<T> mResourceReleaser;
/**
* Construct a new shared-reference that will 'own' the supplied {@code value}.
* The reference count will be set to 1. When the reference count decreases to zero
* {@code resourceReleaser} will be used to release the {@code value}
* @param value non-null value to manage
* @param resourceReleaser non-null ResourceReleaser for the value
*/
public SharedReference(T value, ResourceReleaser<T> resourceReleaser) {
mValue = Preconditions.checkNotNull(value);
mResourceReleaser = Preconditions.checkNotNull(resourceReleaser);
mRefCount = 1;
addLiveReference(value);
}
/**
* Increases the reference count of a live object in the static map. Adds it if it's not
* being held.
*
* @param value the value to add.
*/
private static void addLiveReference(Object value) {
synchronized (sLiveObjects) {
Integer count = sLiveObjects.get(value);
if (count == null) {
sLiveObjects.put(value, 1);
} else {
sLiveObjects.put(value, count + 1);
}
}
}
/**
* Decreases the reference count of live object from the static map. Removes it if it's reference
* count has become 0.
*
* @param value the value to remove.
*/
private static void removeLiveReference(Object value) {
synchronized (sLiveObjects) {
Integer count = sLiveObjects.get(value);
if (count == null) {
// Uh oh.
FLog.wtf(
"SharedReference",
"No entry in sLiveObjects for value of type %s",
value.getClass());
} else if (count == 1) {
sLiveObjects.remove(value);
} else {
sLiveObjects.put(value, count - 1);
}
}
}
/**
* Get the current referenced value. Null if there's no value.
* @return the referenced value
*/
public synchronized T get() {
return mValue;
}
/**
* Checks if this shared-reference is valid i.e. its reference count is greater than zero.
* @return true if shared reference is valid
*/
public synchronized boolean isValid() {
return mRefCount > 0;
}
/**
* Checks if the shared-reference is valid i.e. its reference count is greater than zero
* @return true if the shared reference is valid
*/
public static boolean isValid(SharedReference<?> ref) {
return ref != null && ref.isValid();
}
/**
* Bump up the reference count for the shared reference
* Note: The reference must be valid (aka not null) at this point
*/
public synchronized void addReference() {
ensureValid();
mRefCount++;
}
/**
* Decrement the reference count for the shared reference. If the reference count drops to zero,
* then dispose of the referenced value
*/
public void deleteReference() {
if (decreaseRefCount() == 0) {
T deleted;
synchronized (this) {
deleted = mValue;
mValue = null;
}
mResourceReleaser.release(deleted);
removeLiveReference(deleted);
}
}
/**
* Decrements reference count for the shared reference. Returns value of mRefCount after
* decrementing
*/
private synchronized int decreaseRefCount() {
ensureValid();
Preconditions.checkArgument(mRefCount > 0);
mRefCount--;
return mRefCount;
}
/**
* Assert that there is a valid referenced value. Throw a NullReferenceException otherwise
* @throws NullReferenceException, if the reference is invalid (i.e.) the underlying value is null
*/
private void ensureValid() {
if (!isValid(this)) {
throw new NullReferenceException();
}
}
/**
* A test-only method to get the ref count
* DO NOT USE in regular code
*/
public synchronized int getRefCountTestOnly() {
return mRefCount;
}
/**
* The moral equivalent of NullPointerException for SharedReference. Indicates that the
* referenced object is null
*/
public static class NullReferenceException extends RuntimeException {
public NullReferenceException() {
super("Null shared reference");
}
}
}
这个类中有这么两个重要方法:addReference()和deleteReference(),通过这两个基本方法来对引用进行计数,一旦计数为零时,则调用ResourceReleaser的release方法(调用了如:nativeFree、删除引用或Bitmap.recycle()等)。
在Android5.0后Fresco又封装了CloseableReference类实现了Cloneable、Closeable接口,它在调用.clone()方法时同时会调用addReference()来增加一个引用计数,在调用.close()方法时同时会调用deleteReference()来删除一个引用计数。 fresco对CloseableReference的注释:This class allows reference-counting semantics in a Java-friendlier way. A single object can have any number of CloseableReferences pointing to it. When all of these have been closed, the object either has its {@link Closeable#close} method called, if it implements {@link Closeable}, or its designated {@link ResourceReleaser#release}。 翻译过来就是CloseReferen