android 載入圖片輕鬆避免OOM(out of memory)

l_serein發表於2012-05-08

在使用android載入圖片的時候,經常會出現記憶體溢位,主要是由於android可使用的記憶體太小,而通過程式碼載入進來的圖片,並不會被GC回收,於是我寫了一個工具類用來載入圖片,並且建立快取,輕鬆避免記憶體溢位,廢話不多說,上程式碼

[java] view plaincopy
  1. package l.test1.util;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileNotFoundException;  
  6. import java.io.IOException;  
  7. import java.io.InputStream;  
  8. import java.util.HashMap;  
  9. import java.util.HashSet;  
  10. import java.util.LinkedList;  
  11. import java.util.Map;  
  12. import java.util.Queue;  
  13. import java.util.Set;  
  14.   
  15. import android.graphics.Bitmap;  
  16. import android.graphics.BitmapFactory;  
  17. import android.graphics.BitmapFactory.Options;  
  18.   
  19. /** 
  20.  * Bitmap工具類,快取用過的指定數量的圖片,使用此工具類,不再需要手動管理Bitmap記憶體 原理: 
  21.  * 用一個佇列儲存使用Bitmap的順序,每次使用Bitmap將物件移動到佇列頭 當記憶體不夠,或者達到制定的快取數量的時候,回收佇列尾部圖片 
  22.  * 保證當前使用最多的圖片得到最長時間的快取,提高速度 
  23.  *  
  24.  * @author liaoxingliao 
  25.  *  
  26.  */  
  27. public final class BitMapUtil {  
  28.   
  29.     private static final Size ZERO_SIZE = new Size(00);  
  30.     private static final Options OPTIONS_GET_SIZE = new Options();  
  31.     private static final Options OPTIONS_DECODE = new Options();  
  32.     private static final byte[] LOCKED = new byte[0];  
  33.   
  34.     private static final LinkedList<String> CACHE_ENTRIES = new LinkedList<String>(); // 此物件用來保持Bitmap的回收順序,保證最後使用的圖片被回收  
  35.     private static final Queue<QueueEntry> TASK_QUEUE = new LinkedList<QueueEntry>(); // 執行緒請求建立圖片的佇列  
  36.     private static final Set<String> TASK_QUEUE_INDEX = new HashSet<String>(); // 儲存佇列中正在處理的圖片的key,有效防止重複新增到請求建立佇列  
  37.   
  38.     private static final Map<String, Bitmap> IMG_CACHE_INDEX = new HashMap<String, Bitmap>(); // 快取Bitmap  
  39.                                                                                                 // 通過圖片路徑,圖片大小  
  40.   
  41.     private static int CACHE_SIZE = 200// 快取圖片數量  
  42.   
  43.     static {  
  44.         OPTIONS_GET_SIZE.inJustDecodeBounds = true;  
  45.         // 初始化建立圖片執行緒,並等待處理  
  46.         new Thread() {  
  47.             {  
  48.                 setDaemon(true);  
  49.             }  
  50.   
  51.             public void run() {  
  52.                 while (true) {  
  53.                     synchronized (TASK_QUEUE) {  
  54.                         if (TASK_QUEUE.isEmpty()) {  
  55.                             try {  
  56.                                 TASK_QUEUE.wait();  
  57.                             } catch (InterruptedException e) {  
  58.                                 e.printStackTrace();  
  59.                             }  
  60.                         }  
  61.                     }  
  62.                     QueueEntry entry = TASK_QUEUE.poll();  
  63.                     String key = createKey(entry.path, entry.width,  
  64.                             entry.height);  
  65.                     TASK_QUEUE_INDEX.remove(key);  
  66.                     //createBitmap(entry.path, entry.width, entry.height);  
  67.                     //修正過的程式碼  
  68.                     getBitmap(entry.path,entry.width,entry.height);  
  69.                 }  
  70.             }  
  71.         }.start();  
  72.   
  73.     }  
  74.   
  75.     /** 
  76.      * 建立一張圖片 如果快取中已經存在,則返回快取中的圖,否則建立一個新的物件,並加入快取 
  77.      * 寬度,高度,為了縮放原圖減少記憶體的,如果輸入的寬,高,比原圖大,返回原圖 
  78.      *  
  79.      * @param path      圖片物理路徑 (必須是本地路徑,不能是網路路徑) 
  80.      * @param width     需要的寬度 
  81.      * @param height    需要的高度 
  82.      * @return 
  83.      */  
  84.     public static Bitmap getBitmap(String path, int width, int height) {  
  85.         Bitmap bitMap = null;  
  86.         try {  
  87.             if (CACHE_ENTRIES.size() >= CACHE_SIZE) {  
  88.                 destoryLast();  
  89.             }  
  90.             bitMap = useBitmap(path, width, height);  
  91.             if (bitMap != null && !bitMap.isRecycled()) {  
  92.                 return bitMap;  
  93.             }  
  94.             bitMap = createBitmap(path, width, height);  
  95.             String key = createKey(path, width, height);  
  96.             synchronized (LOCKED) {  
  97.                 IMG_CACHE_INDEX.put(key, bitMap);  
  98.                 CACHE_ENTRIES.addFirst(key);  
  99.             }  
  100.         } catch (OutOfMemoryError err) {  
  101.             destoryLast();  
  102.             System.out.println(CACHE_SIZE);  
  103.             //return createBitmap(path, width, height);  
  104.             //修正過的程式碼  
  105.             return getBitmap(path, width, height);  
  106.         }  
  107.         return bitMap;  
  108.     }  
  109.   
  110.     /** 
  111.      * 設定快取圖片數量 如果輸入負數,會產生異常 
  112.      *  
  113.      * @param size 
  114.      */  
  115.     public static void setCacheSize(int size) {  
  116.         if (size <= 0) {  
  117.             throw new RuntimeException("size :" + size);  
  118.         }  
  119.         while (size < CACHE_ENTRIES.size()) {  
  120.             destoryLast();  
  121.         }  
  122.         CACHE_SIZE = size;  
  123.     }  
  124.   
  125.     /** 
  126.      * 加入一個圖片處理請求到圖片建立佇列 
  127.      *  
  128.      * @param path 
  129.      *            圖片路徑(本地) 
  130.      * @param width 
  131.      *            圖片寬度 
  132.      * @param height 
  133.      *            圖片高度 
  134.      */  
  135.     public static void addTask(String path, int width, int height) {  
  136.         QueueEntry entry = new QueueEntry();  
  137.         entry.path = path;  
  138.         entry.width = width;  
  139.         entry.height = height;  
  140.         synchronized (TASK_QUEUE) {  
  141.             String key = createKey(path, width, height);  
  142.             if (!TASK_QUEUE_INDEX.contains(key)  
  143.                     && !IMG_CACHE_INDEX.containsKey(key)) {  
  144.                 TASK_QUEUE.add(entry);  
  145.                 TASK_QUEUE_INDEX.add(key);  
  146.                 TASK_QUEUE.notify();  
  147.             }  
  148.         }  
  149.     }  
  150.       
  151.     /** 
  152.      * 通過圖片路徑返回圖片實際大小 
  153.      * @param path      圖片物理路徑 
  154.      * @return 
  155.      */  
  156.     public static Size getBitMapSize(String path) {  
  157.         File file = new File(path);  
  158.         if (file.exists()) {  
  159.             InputStream in = null;  
  160.             try {  
  161.                 in = new FileInputStream(file);  
  162.                 BitmapFactory.decodeStream(in, null, OPTIONS_GET_SIZE);  
  163.                 return new Size(OPTIONS_GET_SIZE.outWidth,  
  164.                         OPTIONS_GET_SIZE.outHeight);  
  165.             } catch (FileNotFoundException e) {  
  166.                 return ZERO_SIZE;  
  167.             } finally {  
  168.                 closeInputStream(in);  
  169.             }  
  170.         }  
  171.         return ZERO_SIZE;  
  172.     }  
  173.   
  174.     // ------------------------------------------------------------------ private Methods  
  175.     // 將圖片加入佇列頭  
  176.     private static Bitmap useBitmap(String path, int width, int height) {  
  177.         Bitmap bitMap = null;  
  178.         String key = createKey(path, width, height);  
  179.         synchronized (LOCKED) {  
  180.             bitMap = IMG_CACHE_INDEX.get(key);  
  181.             if (null != bitMap) {  
  182.                 if (CACHE_ENTRIES.remove(key)) {  
  183.                     CACHE_ENTRIES.addFirst(key);  
  184.                 }  
  185.             }  
  186.         }  
  187.         return bitMap;  
  188.     }  
  189.   
  190.     // 回收最後一張圖片  
  191.     private static void destoryLast() {  
  192.         synchronized (LOCKED) {  
  193.             String key = CACHE_ENTRIES.removeLast();  
  194.             if (key.length() > 0) {  
  195.                 Bitmap bitMap = IMG_CACHE_INDEX.remove(key);  
  196.                 if (bitMap != null && !bitMap.isRecycled()) {  
  197.                     bitMap.recycle();  
  198.                     bitMap = null;  
  199.                 }  
  200.             }  
  201.         }  
  202.     }  
  203.   
  204.     // 建立鍵  
  205.     private static String createKey(String path, int width, int height) {  
  206.         if (null == path || path.length() == 0) {  
  207.             return "";  
  208.         }  
  209.         return path + "_" + width + "_" + height;  
  210.     }  
  211.   
  212.     // 通過圖片路徑,寬度高度建立一個Bitmap物件  
  213.     private static Bitmap createBitmap(String path, int width, int height) {  
  214.         File file = new File(path);  
  215.         if (file.exists()) {  
  216.             InputStream in = null;  
  217.             try {  
  218.                 in = new FileInputStream(file);  
  219.                 Size size = getBitMapSize(path);  
  220.                 if (size.equals(ZERO_SIZE)) {  
  221.                     return null;  
  222.                 }  
  223.                 int scale = 1;  
  224.                 int a = size.getWidth() / width;  
  225.                 int b = size.getHeight() / height;  
  226.                 scale = Math.max(a, b);  
  227.                 synchronized (OPTIONS_DECODE) {  
  228.                     OPTIONS_DECODE.inSampleSize = scale;  
  229.                     Bitmap bitMap = BitmapFactory.decodeStream(in, null,  
  230.                             OPTIONS_DECODE);  
  231.                     return bitMap;  
  232.                 }  
  233.             } catch (FileNotFoundException e) {  
  234.                 e.printStackTrace();  
  235.             } finally {  
  236.                 closeInputStream(in);  
  237.             }  
  238.         }  
  239.         return null;  
  240.     }  
  241.       
  242.     // 關閉輸入流  
  243.     private static void closeInputStream(InputStream in) {  
  244.         if (null != in) {  
  245.             try {  
  246.                 in.close();  
  247.             } catch (IOException e) {  
  248.                 e.printStackTrace();  
  249.             }  
  250.         }  
  251.     }  
  252.   
  253.     // 圖片大小  
  254.     static class Size {  
  255.         private int width, height;  
  256.   
  257.         Size(int width, int height) {  
  258.             this.width = width;  
  259.             this.height = height;  
  260.         }  
  261.   
  262.         public int getWidth() {  
  263.             return width;  
  264.         }  
  265.   
  266.         public int getHeight() {  
  267.             return height;  
  268.         }  
  269.     }  
  270.   
  271.     // 佇列快取引數物件  
  272.     static class QueueEntry {  
  273.         public String path;  
  274.         public int width;  
  275.         public int height;  
  276.     }  
  277. }  


測試程式碼使用android1.6完成,需要的可移步下載,地址為:http://download.csdn.net/detail/liaoxingliao/3756187 (這個工程內部的這個工具類由於我的疏忽有幾個地方寫錯了,現在但是那個資源上傳完以後,既不能修改,又不能刪除.有誰知道怎麼改的,希望告訴我一下)

相關文章