在Android 2.3.3(API Level 10)以及以前,Bitmap的backing pixel 數據存儲在native memory, 與Bitmap自己是分開的,Bitmap自己存儲在dalvik heap 中。致使其pixel數據不能判斷是否還須要使用,不能及時釋放,容易引發OOM錯誤。 從Android 3.0(API 11)開始,pixel數據與Bitmap一塊兒存儲在Dalvik heap中。java
結論:android
如何處理圖片來避免OOM異常:
算法
1.在Android 2.3.3以及以前,建議使用Bitmap.recycle()方法,及時釋放資源。數組
2.設置Options.inPreferredConfig值來下降內存消耗 //如把默認值ARGB_8888改成RGB_565,節約一半內存緩存
3.設置Options.inSampleSize 對大圖片進行壓縮
ide
4.設置Options.inPurgeable和inInputShareable:讓系統能及時回收內存。
優化
1)inPurgeable:設置爲True時,表示系統內存不足時能夠被回 收,設置爲False時,表示不能被回收。url
2)inInputShareable:設置是否深拷貝,與inPurgeable結合使用,inPurgeable爲false時,該參數無心義True: share a reference to the input data(inputStream, array,etc) 。 False :a deep copy。spa
5.使用decodeStream代替其餘decodeResource,setImageResource,setImageBitmap等方法:code
//decodeStream直接調用 JNI>>nativeDecodeAsset()來完成decode,無需再使用java層的createBitmap,也不使用java空間進行分辨率適配,雖節省dalvik內存,但須要在hdpi和mdpi,ldpi中配置相應的 圖片資源,不然在不一樣分辨率機器上都是一樣大小(像素點數量)。其餘方法如setImageBitmap、setImageResource、 BitmapFactory.decodeResource在完成decode後,最終都是經過java層的 createBitmap來完成的,須要消耗更多內存。
在android機器上處理圖片時候,常常會遇到各類各樣的狀況致使out of memory錯誤,這種狀況比通常的exception難處理的多,由於你要爲本身所使用的每一分內存負責。遇到這種狀況時候,不能慌,只能慢慢抽繭剝絲一點點處理。
在此次處理過程當中,我整理了一下幾個處理的思路,應該能夠減輕部分的oom症狀。
一、從讀入內存入手。
在讀入圖片時候能夠給BitmapFactory設置各類參數,使得咱們可以對讀入的圖片尺寸作出控制。
FileInputStream fis;
fis = new FileInputStream(path);
int size = fis.available();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inPurgeable = true;
options.inInputShareable = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
if (size > 409600 * 4)
{
options.inSampleSize = (int)Math.sqrt(size / 409600);
bitmap = BitmapFactory.decodeStream(fis, null, options);
}
else
{
bitmap = BitmapFactory.decodeStream(fis, null, options);
}
步驟:
i、先獲取圖片大小,而後根據圖片大小來設置不一樣的縮放比例,因此使用了FileInputStream,能夠獲取圖片的大小。
ii、設置這兩個東西inPurgeable 和inInputShareable 。這裏對inPurgeable作個說明。
若是
inPurgeable
設爲True的話表示使用BitmapFactory建立的Bitmap
用於存儲Pixel的內存空間在系統內存不足時能夠被回收, 在應用須要再次訪問Bitmap的Pixel時(如繪製Bitmap或是調用getPixel),系統會再次調用BitmapFactory
decoder從新生成Bitmap的Pixel數組。 爲了可以從新解碼圖像,bitmap要可以訪問存儲Bitmap的原始數據。
在inPurgeable爲false時表示建立的Bitmap的Pixel內存空間不能被回收,
這樣BitmapFactory在不停decodeByteArray建立新的Bitmap對象,不一樣設備的內存不一樣,所以可以同時建立的Bitmap個數可能有所不一樣, 200個bitmap足以使大部分的設備從新OutOfMemory錯誤。
當isPurgable設爲true時,系統中內存不足時, 能夠回收部分Bitmap佔據的內存空間,這時通常不會出現OutOfMemory
錯誤。
因此設置inPurgeable = true是頗有必要的。這個inInputShareable是和inPurgeable 搭配使用的。
iii、設置讀取色彩。
android中有4中色彩。
ALPHA_8:每一個像素佔用1byte內存
ARGB_4444:每一個像素佔用2byte內存
ARGB_8888:每一個像素佔用4byte內存
RGB_565:每一個像素佔用2byte內存
Android默認的顏色模式爲ARGB_8888,因此咱們採用inPreferredConfig = Bitmap.Config.RGB_565;方式讀取,能夠減小內存消耗,失真在手機查看的狀況下都可以忍受。
iiii、讀取縮略圖進入內存。由於大圖片在手機上處理不須要徹底展現,因此不須要加載所有。我這裏使用了1.6m做爲壓縮的壓縮的基本尺寸,低於這個就不壓縮直接讀取。使用這個尺寸由於須要圖片大小至少爲640*640,對於圖片大小沒有限制的能夠採用更小的尺寸做爲壓縮的基本尺寸。options.inSampleSize這個參數能夠控制讀取的壓縮比例。官方推薦的是使用2的冪次方比例,我這裏也只是使用了普通的壓縮比例。這個須要注意的是這個壓縮是針對寬和高的壓縮,因此對大小是壓縮比例的平方。
另外,這裏吐槽一下,開始使用的是0.8m,結果小米的自帶相機優化,使得按照200k大小取縮略圖後寬高居然不夠640*640了,其餘的手機都是ok的,只能改成1.6m了。
小結:爲了可以讓系統可以處理咱們的圖片,這裏費盡心機的壓縮讀入的圖片尺寸,固然仍是須要在保證需求的前提下的。
二、從使用內存入手
i、及時刪除不用的bitmap。想要儘可能少的使用用內存,要保證在每一個bitmap不使用的時候及時mBitmap.recycle();System.gc();
這個時候要保證這個bitmap是不會再被使用的,也就是新的副本已經被createBitmap或者copy出來了。否則的話,你隨便的recycle會致使當前正在使用的圖片也會被回收,頁面上就沒圖片了,嚴重的還會致使系統在調用圖片時候崩潰。因此之一部須要很細緻。
後面的這個system.gc()只是通知虛擬機能夠回收了,可是虛擬機何時回收還不必定,因此不要寄但願於這個東西會立馬清出你須要的內存。
這裏能夠經過DDMS裏面查看應用的heap使用狀況來確認你的bitmap處理是否正常。
三、從圖片存儲和重複利用入手
i、使用內存緩存和文件緩存處理。由於從內存讀取圖片會比較快,因此爲了更大限度使用內存,使用兩層緩存。硬引用緩存不會輕易被回收,用來保存經常使用數據,不經常使用的轉入軟引用緩存。
private static final int SOFT_CACHE_SIZE = 15; //軟引用緩存容量
private static LruCache mLruCache; //硬引用緩存
private static LinkedHashMap> mSoftCache; //軟引用緩存
public ImageMemoryCache(Context context) {
int memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
int cacheSize = 1024 * 1024 * memClass / 4; //硬引用緩存容量,爲系統可用內存的1/4
mLruCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
if (value != null)
return value.getRowBytes() * value.getHeight();
else
return 0;
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if (oldValue != null)
// 硬引用緩存容量滿的時候,會根據LRU算法把最近沒有被使用的圖片轉入此軟引用緩存
mSoftCache.put(key, new SoftReference(oldValue));
}
};
mSoftCache = new LinkedHashMap>(SOFT_CACHE_SIZE, 0.75f, true) {
private static final long serialVersionUID = 6040103833179403725L;
@Override
protected boolean removeEldestEntry(Entry> eldest) {
if (size() > SOFT_CACHE_SIZE){
return true;
}
return false;
}
};
}
public Bitmap getBitmapFromCache(String url) {
if(url==null){
return null;
}
Bitmap bitmap;
//先從硬引用緩存中獲取
synchronized (mLruCache) {
bitmap = mLruCache.get(url);
if (bitmap != null) {
//若是找到的話,把元素移到LinkedHashMap的最前面,從而保證在LRU算法中是最後被刪除
mLruCache.remove(url);
mLruCache.put(url, bitmap);
return bitmap;
}
}
//若是硬引用緩存中找不到,到軟引用緩存中找
synchronized (mSoftCache) {
SoftReference bitmapReference = mSoftCache.get(url);
if (bitmapReference != null) {
bitmap = bitmapReference.get();
if (bitmap != null) {
//將圖片移回硬緩存
mLruCache.put(url, bitmap);
mSoftCache.remove(url);
return bitmap;
} else {
mSoftCache.remove(url);
}
}
}
return null;
}
public void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
synchronized (mLruCache) {
mLruCache.put(url, bitmap);
}
}
}
public void clearCache() {
mSoftCache.clear();
}
這個都有註釋了,應該均可以看得懂。文件方面就和普通的同樣了。
四、從頁面優化入手
i、使用viewstub替換掉常用的view.visible,viewstub在不顯示的時候會不佔用內存,另一個會佔用內存。這樣能夠減小圖片佔用的內存使用,一樣適用於其餘的內存消耗。
忘記從哪位高人轉載的了,若有侵權請告知