Android: стратегия кэширования изображений и размер памяти - PullRequest
11 голосов
/ 21 июня 2010

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

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

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

Мои загруженные изображения помещаются в hashmap, а объект 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 уже достаточно»

Ответы [ 3 ]

8 голосов
/ 21 июня 2010

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

1 голос
/ 30 сентября 2011

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

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

0 голосов
/ 08 марта 2013

Я искал различные механизмы кэширования для своих масштабированных растровых изображений, как примеры памяти и дискового кэша.Примеры того, где нужно выполнять сложные задачи, поэтому в итоге я создал собственный кэш растровой памяти с помощью 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;
    }
}
...