Android: стратегия кэширования изображений и размер кеша памяти

Я внедряю систему кэширования изображений для кэширования загруженного изображения.

Моя стратегия основана на двухуровневом кеше: Уровень памяти и уровень диска.

Мой класс очень похож на класс, используемый в проекте droidfu

Мои загруженные изображения помещаются в хэш-карту, а объект Bitmap завернутый в объект SoftRererence. Также сохраняется каждое изображение постоянно на диск. Если запрошенное изображение не найдено в Hashmap<String,SoftReference<Bitmap>> он будет искать на диске, читается, а затем возвращается в хэш-карту. В противном случае изображение будет загруженный из сети. Поскольку я храню изображения в физическом устройстве, я добавил чек, чтобы сохранить пространство устройства и остаться под 1М занимаемого пространства:

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 уже достаточно"

Ответ 1

Не делай этого! SoftReference уже достаточно! На самом деле SoftReference предназначен для того, чтобы делать именно то, что вам нужно. Иногда SoftReference не делает то, что вам нужно. Затем вы просто избавляетесь от SoftReference и пишете свою собственную логику управления памятью. Но, насколько вы используете SoftReference, вы не должны беспокоиться о потреблении памяти, SoftReference делает это за вас.

Ответ 2

Я использую одну треть кучи для кэша изображений.

int memoryInMB = activityManager.getMemoryClass();
long totalAppHeap = memoryInMB * 1024 * 1024;
int runtimeCacheLimit =  (int)totalAppHeap/3;
Кстати, о мягкой ссылке в ссылках на Android Soft не работает так, как вы ожидаете. Существует проблема с платформой, которую мягкие ссылки собирают слишком рано, даже если у вас много свободного места.

Отметьте http://code-gotcha.blogspot.com/2011/09/softreference.html

Ответ 3

Я изучал различные механизмы кэширования для моих масштабированных растровых изображений, как примеров памяти, так и дискового кэша. Примеры, которые сложны для моих потребностей, поэтому я закончил создание собственного кеш-памяти с использованием LruCache. Вы можете посмотреть здесь рабочий код здесь или использовать этот код:

Кэш памяти:

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;
    }
}