面对同样的问题,感谢@CommonsWare的讨论。在此发布完整的解决方案,以帮助更多遇到相同问题的人。欢迎进行编辑和评论。祝好!
When should I recycle a bitmap using LRUCache?
当你的位图既没有被缓存,也没有被任何ImageView引用时。
为了维护位图的引用计数,我们需要扩展BitmapDrawable类并添加引用属性。
这个Android示例程序正好回答了这个问题。
DisplayingBitmaps.zip
下面将详细介绍代码。
(don't recycle the bitmaps in the entryRemoved() call).
并不完全正确。
在entryRemoved委托中检查Bitmap是否仍然从任何ImageView引用。
如果没有,请在那里回收它。
反之亦然,即当视图即将被重用或被删除时,检查其位图(如果视图正在被重用,则为先前的位图)是否在缓存中。如果在那里就让它保持不变,否则将其回收。
关键在于我们需要在两个地方都进行检查,无论是否可以回收位图。
我将解释我的具体情况,我在使用LruCache来保存位图。
并在ListView中显示它们。当不再使用时调用位图的recycle方法。
上面提到的样例中的RecyclingBitmapDrawable.java和RecyclingImageView.java是我们需要的核心组件。他们处理得非常好。
他们的setIsCached和setIsDisplayed方法正是我们所需要的。
代码可以在上面提到的样例链接中找到。但是,为了防止未来链接失效或更改,本答案底部也发布了文件的完整代码。还对setImageResource进行了小的修改以检查先前位图的状态。
--- 这是你需要的代码 ---
因此,你的LruCache管理器应该类似于这样。
LruCacheManager.java
package com.example.cache;
import android.os.Build;
import android.support.v4.util.LruCache;
public class LruCacheManager {
private LruCache<String, RecyclingBitmapDrawable> mMemoryCache;
private static LruCacheManager instance;
public static LruCacheManager getInstance() {
if(instance == null) {
instance = new LruCacheManager();
instance.init();
}
return instance;
}
private void init() {
mMemoryCache = new LruCache<String, RecyclingBitmapDrawable>(6 * 1024 * 1024) {
@Override
protected int sizeOf(String key, RecyclingBitmapDrawable bitmapDrawable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
return bitmapDrawable.getBitmap().getByteCount() ;
} else {
return bitmapDrawable.getBitmap().getRowBytes() * bitmapDrawable.getBitmap().getHeight();
}
}
@Override
protected void entryRemoved(boolean evicted, String key, RecyclingBitmapDrawable oldValue, RecyclingBitmapDrawable newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
oldValue.setIsCached(false);
}
};
}
public void addBitmapToMemoryCache(String key, RecyclingBitmapDrawable bitmapDrawable) {
if (getBitmapFromMemCache(key) == null) {
bitmapDrawable.setIsCached(true);
mMemoryCache.put(key, bitmapDrawable);
}
}
public RecyclingBitmapDrawable getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
public void clear() {
mMemoryCache.evictAll();
}
}
而您的ListView/GridView适配器中的getView()应该像往常一样正常显示。
就像您使用setImageDrawable方法在ImageView上设置新图像时一样。
它会在内部检查先前位图的引用计数,并在不在LRU缓存中时内部调用recycle。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
RecyclingImageView imageView;
if (convertView == null) {
imageView = new RecyclingImageView(getActivity());
imageView.setLayoutParams(new GridView.LayoutParams(
GridView.LayoutParams.WRAP_CONTENT,
GridView.LayoutParams.WRAP_CONTENT));
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setPadding(5, 5, 5, 5);
} else {
imageView = (RecyclingImageView) convertView;
}
MyDataObject dataItem = (MyDataObject) getItem(position);
RecyclingBitmapDrawable image = lruCacheManager.getBitmapFromMemCache(dataItem.getId());
if(image != null) {
imageView.setImageDrawable(image);
} else {
loadImage(dataItem.getId(), R.drawable.empty_view);
}
return imageView;
}
这里是您的
RecyclingImageView.java 文件。
package com.example.cache;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.widget.ImageView;
public class RecyclingImageView extends ImageView {
public RecyclingImageView(Context context) {
super(context);
}
public RecyclingImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDetachedFromWindow() {
setImageDrawable(null);
super.onDetachedFromWindow();
}
@Override
public void setImageDrawable(Drawable drawable) {
final Drawable previousDrawable = getDrawable();
super.setImageDrawable(drawable);
notifyDrawable(drawable, true);
notifyDrawable(previousDrawable, false);
}
@Override
public void setImageResource(int resId) {
final Drawable previousDrawable = getDrawable();
super.setImageResource(resId);
notifyDrawable(previousDrawable, false);
}
private static void notifyDrawable(Drawable drawable, final boolean isDisplayed) {
if (drawable instanceof RecyclingBitmapDrawable) {
((RecyclingBitmapDrawable) drawable).setIsDisplayed(isDisplayed);
} else if (drawable instanceof LayerDrawable) {
LayerDrawable layerDrawable = (LayerDrawable) drawable;
for (int i = 0, z = layerDrawable.getNumberOfLayers(); i < z; i++) {
notifyDrawable(layerDrawable.getDrawable(i), isDisplayed);
}
}
}
}
这是您的RecyclingBitmapDrawable.java
package com.example.cache;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
public class RecyclingBitmapDrawable extends BitmapDrawable {
static final String TAG = "CountingBitmapDrawable";
private int mCacheRefCount = 0;
private int mDisplayRefCount = 0;
private boolean mHasBeenDisplayed;
public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
public void setIsDisplayed(boolean isDisplayed) {
synchronized (this) {
if (isDisplayed) {
mDisplayRefCount++;
mHasBeenDisplayed = true;
} else {
mDisplayRefCount--;
}
}
checkState();
}
public void setIsCached(boolean isCached) {
synchronized (this) {
if (isCached) {
mCacheRefCount++;
} else {
mCacheRefCount--;
}
}
checkState();
}
private synchronized void checkState() {
if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
&& hasValidBitmap()) {
Log.d(TAG, "No longer being used or cached so recycling. "
+ toString());
getBitmap().recycle();
}
}
private synchronized boolean hasValidBitmap() {
Bitmap bitmap = getBitmap();
return bitmap != null && !bitmap.isRecycled();
}
}