安卓:图片缓存策略和内存缓存大小

11

我正在实现一个用于缓存下载图片的图像缓存系统。

我的策略基于两级缓存:内存级别和磁盘级别。

我的类与droidfu项目中使用的类非常相似。

我的下载图片被放入哈希表中,并且Bitmap对象被包装在SoftReference对象中。同时,每张图片也会永久保存到磁盘上。如果请求的图片没有在Hashmap<String,SoftReference<Bitmap>>中找到,它将被从磁盘上读取并重新放回哈希表中。否则,该图片将从网络中下载。由于我将图片存储在物理设备内存中,因此我添加了一个检查来保留设备空间并保持占用空间不超过1M。

private void checkCacheUsage() {

        long size = 0;
        final File[] fileList = new File(mCacheDirPath).listFiles();
        Arrays.sort(fileList, new Comparator<File>() {
            public int compare(File f1, File f2) {
                return Long.valueOf(f2.lastModified()).compareTo(
                        f1.lastModified());
            }
        });
        for (File file : fileList) {
            size += file.length();
            if (size > MAX_DISK_CACHE_SIZE) {
                file.delete();
                Log.d(ImageCache.class.getSimpleName(),
                        "checkCacheUsage: Size exceeded  " + size + "("
                                + MAX_DISK_CACHE_SIZE + ") wiping older file {"+file.toString()+"}");
            }
        }

    }

这个方法是在磁盘写入后的某个时间被调用:

Random r = new Random();
        int ra = r.nextInt(10);

        if (ra % 2 == 0){
            checkCacheUsage();
        }

我想要添加的是对HashMap大小的检查,以防它会增长得太多。类似这样的:

private synchronized void checkMemoryCacheUsage(){

            long size = 0;

            for (SoftReference<Bitmap> a : cache.values()) {

                final Bitmap b = a.get();

                if (b != null && ! b.isRecycled()){
                    size += b.getRowBytes() * b.getHeight();
                }

                if (size > MAX_MEMORY_SIZE){
                  //Remove some elements from the cache
                }

            }

            Log.d(ImageCache.class.getSimpleName(),
                    "checkMemoryCacheUsage: " + size + " in memory");

    }

我的问题是: 什么是正确的MAX_MEMORY_SIZE值? 此外,这种方法可行吗? 一个好的回答可能是:"不要这样做!SoftReference已经足够了"


你也可以考虑使用这个API:http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/CacheBuilder.html - carfield
3个回答

8

不要这么做!SoftReference已经足够了!实际上,SoftReference的设计就是为了满足你的需求。有时候SoftReference不能满足你的需求。那么你可以摆脱SoftReference并编写自己的内存管理逻辑。但只要使用SoftReference,你就不必担心内存消耗,因为SoftReference会为你处理。


Android 实现了软引用,但这种缓存方式不太适合。请参阅此错误报告以获取更多信息:http://code.google.com/p/android/issues/detail?id=20015&can=1&q=softReference&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars。只有通过 SoftReference 可达的图像将在被移除时从内存中删除。Google团队建议使用LRU缓存,问题在于确定缓存的最大大小。 - Janusz
@Janusz,非常正确。但由于没有LRU缓存实现供每个初学者使用和重用,他们必须从SoftReference开始。它并不完美(我知道你所说的问题),但足以作为起点。 - Fedor
我认为这还不够好以开始使用。目前内存释放的速度非常快,软引用毫无用处,反而会造成很多混乱。使用软引用绝对没有任何好处。 - Janusz
@Janusz 是的,你可能是对的。那么我们应该想出一个非常基本的方法,可以推荐给初学者。 - Fedor
今天看到了这个,还没有测试过。http://blog.wu-man.com/2012/01/lrucache-with-softreference-on-android.html - Mathias Conradt
1
谁在寻求“初学者”的解决方案?我认为问题是关于正确的解决方案。如果系统仅因为它们没有被缓存以外的其他内容引用而删除图像,那么这不是一个正确的解决方案。 - User

1

我正在使用堆的三分之一作为图像缓存。

int memoryInMB = activityManager.getMemoryClass();
long totalAppHeap = memoryInMB * 1024 * 1024;
int runtimeCacheLimit =  (int)totalAppHeap/3;

顺便提一下,关于软引用,在Android中,软引用并不像你期望的那样工作。存在一个平台问题,即使有足够的空闲内存,软引用也会被过早地回收。
请查看http://code-gotcha.blogspot.com/2011/09/softreference.html

0
我一直在研究不同的缓存机制,以处理我的缩放位图,包括内存和磁盘缓存示例。但这些示例对我的需求来说过于复杂,因此我最终使用 LruCache 制作了自己的位图内存缓存。 您可以查看一个工作代码示例here或使用以下代码:

内存缓存:

public class Cache {
    private static LruCache<Integer, Bitmap> bitmaps = new BitmapLruCache();

    public static Bitmap get(int drawableId){
        Bitmap bitmap = bitmaps.get(drawableId);
        if(bitmap != null){
            return bitmap;  
        } else {
            bitmap = SpriteUtil.createScaledBitmap(drawableId);
            bitmaps.put(drawableId, bitmap);
            return bitmap;
        }
    }
}

BitmapLruCache:

public class BitmapLruCache extends LruCache<Integer, Bitmap> {
    private final static int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    private final static int cacheSize = maxMemory / 2;

    public BitmapLruCache() {
        super(cacheSize);
    }

    @Override
    protected int sizeOf(Integer key, Bitmap bitmap) {
        // The cache size will be measured in kilobytes rather than number of items.
        return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接