2010-06-21 20 views
11

Wdrażam system pamięci podręcznej obrazu do buforowania pobranego obrazu.Android: Strategia pamięci podręcznej obrazu i rozmiar pamięci podręcznej

Moja strategia oparta jest na dwustopniowej pamięci podręcznej: Poziom pamięci i poziom dysku.

Moja klasa jest bardzo podobna do tej klasy stosowanych w droidfu project

Moje zdjęcia zostaną umieścić w hashmap i Objet Bitmap jest owinięty wewnątrz obiektu SoftRererence. Również każdy obraz jest zapisywany na stałe na dysku. Jeśli żądany obraz nie zostanie znaleziony w pliku Hashmap<String,SoftReference<Bitmap>>, zostanie przeszukany na dysku, odczytany , a następnie z powrotem przeniesiony do mapy mieszającej. W przeciwnym razie obraz zostanie pobrany z sieci przez . Odkąd przechowywać obrazy do phisical momery urządzenia, dodałem czek na zachowanie przestrzeni urządzenia i pozostawać pod 1M zajętej przestrzeni:

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()+"}"); 
      } 
     } 

    } 

Metoda ta nazywana jest czasami afte piśmie dysku:

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

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

To, co chciałbym dodać, to taka sama kontrola rozmiaru HashMap, aby zapobiec jej nadmiernemu wzrostowi. Coś takiego:

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"); 

    } 

Moje pytanie brzmi: Co może być słuszną wartość MAX_MEMORY_SIZE? Co więcej, czy to dobre podejście? Dobra odpowiedź może również brzmieć: "Nie rób tego! SoftReference już wystarcza"

+0

Można również rozważyć tę API http: //docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/CacheBuilder.html – carfield

Odpowiedz

8

Nie rób tego! SoftReference już wystarcza! Właściwie SoftReference został zaprojektowany tak, aby robić dokładnie to, czego potrzebujesz. Czasami SoftReference nie robi tego, czego potrzebujesz. Następnie po prostu pozbyć SoftReference i napisać własną logikę zarządzania pamięcią. Ale jeśli chodzi o SoftReference, nie powinieneś martwić się o zużycie pamięci, SoftReference robi to za Ciebie.

+0

Android wprowadza miękkie referencje w sposób, który sprawia, że nie nadaje się do tego rodzaju buforowania. Więcej informacji znajdziesz w tym raporcie o błędach: http://code.google.com/p/android/issues/detail?id=20015&can=1&q=softReference&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars. Obrazy zostaną usunięte z pamięci w momencie, w którym są osiągalne tylko poprzez SoftReference. Zespół Google zaleca korzystanie z pamięci podręcznej LRU. Problem polega na określeniu maksymalnego rozmiaru pamięci podręcznej. – Janusz

+0

@Janusz, To prawda. Ale ponieważ nie ma implementacji pamięci podręcznej LRU, którą każdy początkujący może podjąć i ponownie użyć, muszą zacząć od SoftReference. Nie jest doskonały (jestem świadomy problemu, o którym mówisz), ale na początek wystarczy. – Fedor

+0

Nie sądzę, że na początku wystarczy goog. Przy prędkości, z jaką pamięć zostaje uwolniona, miękkie referencje są bezużyteczne i powodują wiele zamieszania. Nie ma absolutnie nic, co można by z nich wykorzystać. – Janusz

1

Używam jednej trzeciej sterty dla pamięci podręcznej obrazu.

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

Nawiasem mówiąc, o miękkim odwołaniu, w Android Soft odniesienia nie działają zgodnie z oczekiwaniami. Występuje problem z platformą, że miękkie referencje są zbierane zbyt wcześnie, nawet gdy jest dużo wolnej pamięci.

Sprawdź http://code-gotcha.blogspot.com/2011/09/softreference.html

0

Szukałem w różnych mechanizmów buforowania dla mojego skalowane bitmapy, obu przykładach i pamięci podręcznej dysku. Przykłady, w których były skomplikowane dla moich potrzeb, więc w końcu stworzyłem własną pamięć podręczną bitmapy za pomocą LruCache. Można spojrzeć na roboczej kodowej przykład here lub użyć tego kodu: cache

pamięci:

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; 
    } 
} 
Powiązane problemy