標籤:
每當碰到一些大圖片的時候,我們如果不對圖片進行處理就會報OOM異常,
這個問題曾經讓我覺得很煩惱,後來終於得到瞭解決,
那麼現在就讓我和大家一起分享一下吧。
這篇博文要講的圖片緩衝機制,我接觸到的有兩鐘,一種是軟引用,另一種是記憶體緩衝技術。
先來看下兩者的使用方式,再來作比較。
除了載入圖片時要用到緩衝處理,還有一個比較重要的步驟要做,就是要先壓縮圖片。
1、壓縮圖片
至於要壓縮到什麼狀態就要看自己當時的處境了,壓縮圖片的時候既要達到一個小的值,又不能讓其模糊
,更不能展開圖片。
/**
* 載入記憶卡圖片
*/
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; // 設定了此屬性一定要記得將值設定為false
Bitmap bitmap = null;
bitmap = BitmapFactory.decodeFile(url, options);
int be = (int) ((options.outHeight > options.outWidth ? options.outHeight / 150
: options.outWidth / 200));
if (be <= 0) // 判斷200是否超過原始圖片高度
be = 1; // 如果超過,則不進行縮放
options.inSampleSize = be;
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
options.inPurgeable = true;
options.inInputShareable = true;
options.inJustDecodeBounds = false;
try {
bitmap = BitmapFactory.decodeFile(url, options);
} catch (OutOfMemoryError e) {
System.gc();
Log.e(TAG, "OutOfMemoryError");
}
2、軟引用:
只要有足夠的記憶體,就一直保持對象,直到發現記憶體吃緊且沒有Strong Ref時才回收對象。
我們可以這樣定義:map裡面的鍵是用來放圖片地址的,既可以是網路上的圖片地址,也可以SDcard上的圖片地址,
map裡面的值裡面放的是持有軟引用的Bitmap,當然如果你要放Drawable,那也是可以的。
private Map<String, SoftReference<Bitmap>> imageMap
= new HashMap<String, SoftReference<Bitmap>>();
接下來就讓我再介紹一下如何具體載入圖片:
步驟:(1)先通過URL查看緩衝中是否有圖片,如果有,則直接去緩衝中取得。
如果沒有,就開線程重新去網上下載。
(2)下載完了之後,就把圖片放在緩衝裡面,方便下次可以直接從緩衝中取得。
public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
SoftReference<Bitmap> reference = imageMap.get(imageUrl);
if(reference != null) {
if(reference.get() != null) {
return reference.get();
}
}
final Handler handler = new Handler() {
public void handleMessage(final android.os.Message msg) {
//加入到緩衝中
Bitmap bitmap = (Bitmap)msg.obj;
imageMap.put(imageUrl, new SoftReference<Bitmap>(bitmap));
if(imageCallBack != null) {
imageCallBack.getBitmap(bitmap);
}
}
};
new Thread(){
public void run() {
Message message = handler.obtainMessage();
message.obj = downloadBitmap(imageUrl);
handler.sendMessage(message);
}
}.start();
return null ;
}
// 從網上下載圖片
private Bitmap downloadBitmap (String imageUrl) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
return bitmap ;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public interface ImageCallBack{
void getBitmap(Bitmap bitmap);
}
3、記憶體緩衝技術
另外一種圖片緩衝的方式就是記憶體緩衝技術。在Android中,有一個叫做LruCache類專門用來做圖片緩衝處理的。
它有一個特點,當緩衝的圖片達到了預先設定的值的時候,那麼近期使用次數最少的圖片就會被回收掉。
步驟:(1)要先設定緩衝圖片的記憶體大小,我這裡設定為手機記憶體的1/8,
手機記憶體的擷取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);
(2)LruCache裡面的索引值對分別是URL和對應的圖片
(3)重寫了一個叫做sizeOf的方法,返回的是圖片數量。
private LruCache<String, Bitmap> mMemoryCache;
private LruCacheUtils() {
if (mMemoryCache == null)
mMemoryCache = new LruCache<String, Bitmap>(
MAXMEMONRY / 8) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// 重寫此方法來衡量每張圖片的大小,預設返回圖片數量。
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
@Override
protected void entryRemoved(boolean evicted, String key,
Bitmap oldValue, Bitmap newValue) {
Log.v("tag", "hard cache is full , push to soft cache");
}
};
}
(4)下面的方法分別是清空緩衝、添加圖片到緩衝、從緩衝中取得圖片、從緩衝中移除。
移除和清除緩衝是必須要做的事,因為圖片緩衝處理不當就會報記憶體溢出,所以一定要引起注意。
public void clearCache() {
if (mMemoryCache != null) {
if (mMemoryCache.size() > 0) {
Log.d("CacheUtils",
"mMemoryCache.size() " + mMemoryCache.size());
mMemoryCache.evictAll();
Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
}
mMemoryCache = null;
}
}
public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (mMemoryCache.get(key) == null) {
if (key != null && bitmap != null)
mMemoryCache.put(key, bitmap);
} else
Log.w(TAG, "the res is aready exits");
}
public synchronized Bitmap getBitmapFromMemCache(String key) {
Bitmap bm = mMemoryCache.get(key);
if (key != null) {
return bm;
}
return null;
}
/**
* 移除緩衝
*
* @param key
*/
public synchronized void removeImageCache(String key) {
if (key != null) {
if (mMemoryCache != null) {
Bitmap bm = mMemoryCache.remove(key);
if (bm != null)
bm.recycle();
}
}
}
4、兩者的比較
說到這裡,我覺得有必要來進行一下比較了。
網上有很多人使用軟引用載入圖片的多 ,但是現在已經不再推薦使用這種方式了,
(1)因為從 Android 2.3 (API Level 9)開始,記憶體回收行程會更傾向於回收持有軟引用或弱引用的對象,
這讓軟引用和弱引用變得不再可靠。
(2)另外,Android 3.0 (API Level 11)中,圖片的資料會儲存在本地的記憶體當中,
因而無法用一種可預見的方式將其釋放,這就有潛在的風險造成應用程式的記憶體溢出並崩潰,
所以我這裡用得是LruCache來緩衝圖片,當儲存Image的大小大於LruCache設定的值,系統自動釋放記憶體,
這個類是3.1版本中提供的,如果你是在更早的Android版本中開發,則需要匯入android-support-v4的jar包。
後記:我一直有強調一件事件,就是人應該要不停地進步,沒有人生來就會編碼,
更沒有人一開始就能找到很好的解決方案,我介紹了這兩種用法,
轉--圖片緩衝之記憶體緩衝技術LruCache,軟引用