2011-07-29 95 views
8

我認爲這是問及以前問,但仍然有些事情我不明白。Android圖像緩存 - 如何?

我已經嘗試了兩種不同的方法:

  1. 保存在內存中,所有圖像時一定限度時開始超出,開始刪除它們
  2. 允許Android修復此與SoftReferences

在2.它只是清理它們,有時是我分配它們的第二個!我不會分配太多 - 50 - 50像素的30 - 40圖像。

所以我堅持一個。問題是什麼是限制?

  1. 我可以從設備獲得一些可靠的信息,我已經離開了多少位圖存儲器嗎?我已經做了一些研究,看DDMS值,它只是佔用了越來越多的空間(如果我沒有清理),直到爆炸。一時只剩200K,接下來系統提供2M更多...
  2. 我目前正在使用一些基於設備模型或屏幕尺寸的啓發式決策。從長遠來看,我認爲這是沒有根據的。某些手機​​有內存異常,其他手機完全免費。
  3. 有沒有第三種解決方案,正確的?
+0

尺寸爲50x50的30-40張圖片不應導致OOME。 也許你加載更大的?如果是這樣,你可以縮小它們。 此外,如果某些圖像從SoftReference中清除,它們不會被使用,所以只需在必要時重新加載它們即可。 –

回答

8

最重要的問題:是否你recycle()-你的位圖?這對於薑餅應用程序非常重要(可能也是後薑餅)。

看着Memory Management for Android Apps session from Google I/O 2011幫助我更好地瞭解Android開發的特點。

該視頻提到了一個名爲MAT - a Memory Analyzer的工具 - 這對於確定您的內存中是否存在已泄漏的對象十分有用。你可能比你認爲的30-40多。

爲了查看和/或記錄堆的當前大小等,我建議使用this answer about Android Out of Memory exceptions中的代碼。

+0

謝謝你的鏈接。我試圖回收它們,但是它們被用在某些視圖中,並且當我回到它們所使用的活動時會導致崩潰。 – Danail

2

該限制與其所運行設備上的VM堆大小有關,因爲這是不同的設備和操作系統,它可以從16MB(應用程序總數)到256MB +(在平板電腦上)。

您需要將其保留在較低端,爲每個設備創建不同的版本,或讓應用程序在運行時檢查允許的內容並相應地加載您的圖像。

有檢查堆中的自由空間量的方法,它的大小:

這個API裁判會幫助你的即時拍攝方法:

http://developer.android.com/reference/android/app/ActivityManager.html#getMemoryClass

+0

嗯。我應該在那裏使用什麼? MemoryInfo?它對我來說看起來有點剋制,是關於本地堆的嗎?還是正常的堆?到目前爲止,我已經使用了Debug.getNativeHeapFreeSize(),但它不可靠 - 它提供了TEMPORARY最大限制的大小,如果應用程序想要更多的位圖內存,可以提高更多。 – Danail

+0

除非您明確使用NDK本地分配,否則您不想使用本機堆信息。 Java VM提供了一個堆大小,其中一些堆大小可以由某些工廠調用本地分配,比如位圖。這是從您分配的VM堆大小中扣除的,即使它是本地的,所以如果VM堆爲16,您可以本地分配4MB,那麼您將在VM堆中擁有剩餘的12個。 getMemoryClass()和getLargeMemoryClass()將爲您提供正在運行的設備所允許的VM堆「class」。這是你的應用程序將被允許分配的最大值。 – Hamid

4

圖像緩存是我構建並放入應用商店的應用程序的重要組成部分。該應用程序需要下載圖像並將其緩存在內存和SD卡上,以便其範圍超出一次運行。

總的想法是將圖像添加到緩存管理器中,該緩存管理器既通過基於元數據的密鑰將圖像存儲在關聯容器(HashMap)中,又b)將圖像文件寫入SD卡。

在內存不足的情況下,我釋放HashMap。但仍然可以從SD_Card中檢索圖像,並再次緩存到內存中。

我能夠做到這一點,沒有回收,仍然沒有看到內存問題。據我瞭解,回收並不是必需的,但有助於獲得用於「位圖」的內存的早期版本,因爲在薑餅操作系統前的位圖分配使用本機內存。即不是Dalvik堆的一部分的內存。所以垃圾收集器不釋放這些內存,而是由實現特定的策略釋放。

這是從Cache_Manager類:

public static synchronized void addImage(Bitmap b, String urlString, boolean bSaveToFile, IMAGE_TYPES eIT, boolean bForce) 
{ 
    String szKey = getKeyFromUrlString(urlString, eIT); 

    if (false == m_hmCachedImages.containsKey(szKey) || bForce) 
    { 
     m_hmCachedImages.put(szKey, b); 
     if (bSaveToFile) 
     { 
      boolean bIsNull = false; 
      // Write a null object to disk to prevent future query for non-existent image. 
      if (null == b) 
      { 
       try 
       { 
        bIsNull = true; 
        b = getNullArt(); 
       } 
       catch (NullPointerException e) 
       { 
        e.printStackTrace(); 
        throw e; 
       } 
      } 

      // Don't force null art to disk 
      if (false == File_Manager.imageExists(szKey) || (bForce && bIsNull == false)) 
       File_Manager.writeImage(b, szKey); 
     } 
    } 
} 

從File_Manager類//這裏writeImage(的實例)

public static void writeImage(Bitmap bmp, String szFileName) 
{ 
    checkStorage(); 

    if (false == mExternalStorageWriteable) 
    { 
     Log.e("FileMan", "No Writable External Device Available"); 
     return; 
    } 

    try 
    { 
     // Create dirctory if doesn't exist 
     String szFilePath = getFilesPath(); 
     boolean exists = (new File(szFilePath)).exists(); 
     if (!exists) 
     { 
      new File(szFilePath).mkdirs(); 
     } 

     // Create file 
     File file = new File(szFilePath, szFileName); 

     // Write to file 
     FileOutputStream os = new FileOutputStream(file); 
     bmp.compress(Bitmap.CompressFormat.PNG, 90, os); 
    } catch (IOException e) 
    { 
     // Unable to create file, likely because 
     // external storage is 
     // not currently mounted. 
     Log.e("FileMan", "Error writing file", e); 
    } catch (Exception e) 
    { 
     e.printStackTrace(); 
     throw e; 
    } 
} 
+0

與我的情況幾乎相同,事情是它可能提供低內存條件。當然有機會我有內存泄漏。 – Danail

-2

你可以使用一個WebView來顯示圖像,它有高速緩存內置。另外,它支持2指縮放和滾動,無需任何特殊代碼。