標籤:
前些日子一直為圖片記憶體溢出問題困擾著,查了N多資料,將google徹底翻遍了都沒找到解決方案,就當我幾乎絕望的時候意外發現了一位網友的一個工具類,抱著最後一絲希望將代碼co過來試了一把,結果令我喜出望外。嘿,解決了!暫不說多麼歡喜了,聽我慢慢道來這其中的前因後果吧!
需求:下載時候將圖片一併down下來,在空間裡顯示並支援離線觀看
第一個版本代碼:
//從本地讀取圖片
public Bitmap getBitmapFromSD(String filename) {
FileInputStream fi = null;
BufferedInputStream bi = null;
Bitmap bp = null;
try {
fi = new FileInputStream(filename);
bi = new BufferedInputStream(fi);
bp = BitmapFactory.decodeStream(bi);
} catch (IOException e) {
bp = null;
} finally {
try {
if (bi != null) {
bi.close();
}
if (fi != null) {
fi.close();
}
} catch (IOException e) {
bp = null;
}
}
return bp;
}
問題出現了,由於顯示的圖片過大,所以會出現OutOfMemoryException。我就設想能否捕捉異常來回收圖片再重新載入,於是欲從網上找解決辦法,什麼手動幹預GC,什麼將圖片弱化什麼使用弱引用儲存圖片,有些總結得特別好(http://mzh3344258.blog.51cto.com/1823534/804237),這些方法我一一嘗試可問題仍然未解決。不斷的OOM,不斷的嘗試recycle,錯誤倒是不出現,可一旦記憶體吃不消就會顯示不了圖片,出現的都是預設圖片。最終我從網上找到如下工具類,助我很好的解決了此問題,具體網址忘記了(得謝謝那位網友啦(*^__^*) ),現在代碼貼出來以便下次順手拈來
public final class BitMapUtil {
private static final Size ZERO_SIZE = new Size(0, 0);
private static final Options OPTIONS_GET_SIZE = new Options();
private static final Options OPTIONS_DECODE = new Options();
private static final byte[] LOCKED = new byte[0];
// 此對象用來保持Bitmap的回收順序,保證最後使用的圖片被回收
private static final LinkedList CACHE_ENTRIES = new LinkedList();
// 線程請求建立圖片的隊列
private static final Queue TASK_QUEUE = new LinkedList();
// 儲存隊列中正在處理的圖片的key,有效防止重複添加到請求建立隊列
private static final Set TASK_QUEUE_INDEX = new HashSet();
// 緩衝Bitmap
private static final Map IMG_CACHE_INDEX = new HashMap(); // 通過圖片路徑,圖片大小
private static int CACHE_SIZE = 20; // 緩衝圖片數量
static {
OPTIONS_GET_SIZE.inJustDecodeBounds = true;
// 初始化建立圖片線程,並等待處理
new Thread() {
{
setDaemon(true);
}
public void run() {
while (true) {
synchronized (TASK_QUEUE) {
if (TASK_QUEUE.isEmpty()) {
try {
TASK_QUEUE.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
QueueEntry entry = TASK_QUEUE.poll();
String key = createKey(entry.path, entry.width,
entry.height);
TASK_QUEUE_INDEX.remove(key);
createBitmap(entry.path, entry.width, entry.height);
}
}
}.start();
}
public static Bitmap getBitmap(String path, int width, int height) {
if(path==null){
return null;
}
Bitmap bitMap = null;
try {
if (CACHE_ENTRIES.size() >= CACHE_SIZE) {
destoryLast();
}
bitMap = useBitmap(path, width, height);
if (bitMap != null && !bitMap.isRecycled()) {
return bitMap;
}
bitMap = createBitmap(path, width, height);
String key = createKey(path, width, height);
synchronized (LOCKED) {
IMG_CACHE_INDEX.put(key, bitMap);
CACHE_ENTRIES.addFirst(key);
}
} catch (OutOfMemoryError err) {
destoryLast();
System.out.println(CACHE_SIZE);
return createBitmap(path, width, height);
}
return bitMap;
}
public static Size getBitMapSize(String path) {
File file = new File(path);
if (file.exists()) {
InputStream in = null;
try {
in = new FileInputStream(file);
BitmapFactory.decodeStream(in, null, OPTIONS_GET_SIZE);
return new Size(OPTIONS_GET_SIZE.outWidth,
OPTIONS_GET_SIZE.outHeight);
} catch (FileNotFoundException e) {
return ZERO_SIZE;
} finally {
closeInputStream(in);
}
}
return ZERO_SIZE;
}
// ------------------------------------------------------------------ private Methods
// 將圖片排入佇列頭
private static Bitmap useBitmap(String path, int width, int height) {
Bitmap bitMap = null;
String key = createKey(path, width, height);
synchronized (LOCKED) {
bitMap = IMG_CACHE_INDEX.get(key);
if (null != bitMap) {
if (CACHE_ENTRIES.remove(key)) {
CACHE_ENTRIES.addFirst(key);
}
}
}
return bitMap;
}
// 回收最後一張圖片
private static void destoryLast() {
synchronized (LOCKED) {
String key = CACHE_ENTRIES.removeLast();
if (key.length() > 0) {
Bitmap bitMap = IMG_CACHE_INDEX.remove(key);
if (bitMap != null && !bitMap.isRecycled()) {
bitMap.recycle();
bitMap = null;
}
}
}
}
// 建立鍵
private static String createKey(String path, int width, int height) {
if (null == path || path.length() == 0) {
return "";
}
return path + "_" + width + "_" + height;
}
// 通過圖片路徑,寬度高度建立一個Bitmap對象
private static Bitmap createBitmap(String path, int width, int height) {
File file = new File(path);
if (file.exists()) {
InputStream in = null;
try {
in = new FileInputStream(file);
Size size = getBitMapSize(path);
if (size.equals(ZERO_SIZE)) {
return null;
}
int scale = 1;
int a = size.getWidth() / width;
int b = size.getHeight() / height;
scale = Math.max(a, b);
synchronized (OPTIONS_DECODE) {
OPTIONS_DECODE.inSampleSize = scale;
Bitmap bitMap = BitmapFactory.decodeStream(in, null,
OPTIONS_DECODE);
return bitMap;
}
} catch (FileNotFoundException e) {
Log.v("BitMapUtil","createBitmap=="+e.toString());
} finally {
closeInputStream(in);
}
}
return null;
}
// 關閉輸入資料流
private static void closeInputStream(InputStream in) {
if (null != in) {
try {
in.close();
} catch (IOException e) {
Log.v("BitMapUtil","closeInputStream=="+e.toString());
}
}
}
// 圖片大小
static class Size {
private int width, height;
Size(int width, int height) {
this.width = width;
this.height = height;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
// 隊列緩衝參數對象
static class QueueEntry {
public String path;
public int width;
public int height;
}
}
在使用時我只調用了getBitmap方法,將需要設定的高度寬度以及本地圖片路徑傳遞過去就能自動返回bitmap給我,而且當捕捉到OOMError的時候將LinkedList的最後一張圖片也就是最先存的圖片進行溢出並回收就大功告成,特別注意的是這裡捕捉錯誤Exception是擷取不到的,一定要手動捕獲OutOfMemoryError你才能進行處理(估計這些道理大家都懂得,所以不贅述啦,童鞋們加油!辦法總比困難多o(∩_∩)o )
[轉]android端讀取本地圖片出現OutOfMemoryException