這個是前同事留下來的文章
為什么要撩BitMap?
做android必然會撩到BitMap擅耽,撩過BitMap的都知道盗飒,BitMap可不是你想撩就能撩挨摸,她脾氣可是很不好的栅炒,是一言不合就OOM.So 學(xué)好撩妹姿勢是多么的重要
歷史演進(jìn)
言歸正傳帘不,BitMap會出現(xiàn)OOM其實是有歷史原因的:
在Android 2.2 (api level 8)以及之前说莫,當(dāng)垃圾回收和線程是不能并發(fā)的,當(dāng)發(fā)生垃圾回收的時候寞焙,線程就會被暫停储狭,這就會導(dǎo)致延遲滯后甚至卡頓,系統(tǒng)效率低下捣郊。從Android 2.3開始辽狈,添加了并發(fā)垃圾回收的機制, 這意味著在一個Bitmap不再被引用之后呛牲,它所占用的內(nèi)存會被立即回收刮萌。
在Android 2.3.3 (API level 10)以及之前, 一個Bitmap的像素級數(shù)據(jù)(pixel data)是存放在Native內(nèi)存空間中的。 這些數(shù)據(jù)與Bitmap本身是隔離的娘扩,Bitmap本身被存放在Dalvik堆中着茸。我們無法預(yù)測在Native內(nèi)存中的像素級數(shù)據(jù)何時會被釋放,這意味著程序容易超過它的內(nèi)存限制并且崩潰畜侦。 自Android 3.0 (API Level 11)開始元扔, 像素級數(shù)據(jù)則是與Bitmap本身一起存放在Dalvik堆中。
介于現(xiàn)在已經(jīng)minSdk至少在14以上了,所以接下來就不討論手動回收的問題旋膳;
BitMap在到底占多大澎语?
-
在android 中其實是可以通過api方法來調(diào)用獲取BitMap大小的
public final int getByteCount() { // int result permits bitmaps up to 46,340 x 46,340 return getRowBytes() * getHeight(); }
舉個栗子在nexus 5x上一張xxhdpi下512*512大小的PNG圖片所生成的BitMap 占用大小是802816 在xxxhdpi下占用內(nèi)存是451584 至于怎么算的具體請看http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=498 這個鏈接有詳細(xì)解答,涉及到一些native c代碼的跟蹤,還是值得學(xué)習(xí)一下擅羞; 那么在nexus 5x 上的density 是420(為什么不是480 不是號稱是1920X1080?)在xxhdpi上應(yīng)該是這么算的
512/480 * 420 * 512/480 * 420 * 4 = 802816
計算的過程會有精度丟失尸变,我覺得可以忽略不計算了,在BitMapFactory.cpp中有這么一個計算規(guī)則
scaledWidth = int(scaledWidth * scale + 0.5f); scaledHeight = int(scaledHeight * scale + 0.5f);
至于為什么會乘以4,那就看源碼吧:
static const uint8_t gSize[] = {
0, // Unknown
1, // Alpha_8
2, // RGB_565
2, // ARGB_4444
4, // RGBA_8888
4, // BGRA_8888
1, // kIndex_8
};
小結(jié)
BitMap 的大小涉及到的因素有:
轉(zhuǎn)換色彩的格式是RGB_565 還是ARGB_888等减俏;
所存放資源文件的draweble資源目錄召烂;
機器本身的像素密度,密度越大娃承,所占內(nèi)存越大奏夫;
防止她紅杏出墻(OOM)
BitMap內(nèi)存管理
防止BitMap OOM 就應(yīng)該先了解系統(tǒng)對BitMap的管理,請移駕到:
https://developer.android.com/training/displaying-bitmaps/manage-memory.html
英文和我一樣爛的同學(xué)請看http://hukai.me/android-training-course-in-chinese/graphics/displaying-bitmaps/manage-memory.html
將oom 降低到最小發(fā)生幾率
由于android以及第三方手機廠商平臺的差異化历筝,oom永遠(yuǎn)都會是crash上的承镏纾客;
圖片加載的問題目前開源社區(qū)有了很多優(yōu)秀的depends,例如:fresco ,picaso, glide 梳猪。我個人不建議自己從零開始造輪子寫加載庫麻削,但是可以去查看他們的源碼,學(xué)習(xí)他的實現(xiàn)方式春弥;
如果很不幸呛哟,App中會隔三差五來個OOM 那個不要慌張:
- 操作BitMapFactory
BitmapFactory.Options提供一些附加屬性來指定decode的選項,解析Bitmap時用到2個重要參數(shù): 1.inJustDecodeBounds 設(shè)置為true后匿沛,decode方法解析Bitmap時會返回一個null扫责,只講這個圖片的原始大小(單位是像素)存入BitmapFactory.Options對象的options.outHeight和options.outWidth中俺祠,這樣可以在不分配內(nèi)存的情況下得到圖片的尺寸信息公给。 2.inSampleSize 這個參數(shù)代表縮小比例,如果是1蜘渣,代表原始尺寸淌铐,如果>1,假設(shè)為2,則縮小后圖片像素值為原圖的1/4(長1/2蔫缸,寬1/2)腿准,同等格式下,占用內(nèi)存也變?yōu)樵瓉淼?/4拾碌。decoder以2的冪作為系數(shù)吐葱,接近2的冪的數(shù)值都會被處理為最接近的2的冪值,3.4~4校翔,2.1~2弟跑,這樣。
縮小比例值
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
在調(diào)用以上方法前防症,記得設(shè)置options.inJustDecodeBounds = true; 調(diào)用后算出比例后孟辑,則調(diào)用BitmapFactory.decodexxxx(res, resId, options);
舉個栗子
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
就可以mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
- 如果非常不幸的是你要操作(緩存)很多BitMap實例(這是有多糟糕)那么請用LruCache;LruCache 采用Lru算法將BitMap用鏈表的方式存入緩存哎甲,可以有效的控制BitMap 大小
int cacheSize = 4 * 1024 * 1024; // 4MiB 一般是可用內(nèi)存的1/8
LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
}
-
簡單粗暴--捕捉錯誤 其實從根源上來說這不是解決OOM的辦法,這是一個不是辦法的辦法饲嗽,可以有效規(guī)避程序的崩潰炭玫,從而不至于嚴(yán)重影響程序的崩潰;
try { ...... } catch (OutOfMemoryError e) { e.printStackTrace(); ...... }
更詳細(xì)的Bitmap處理 [http://jayfeng.com/2016/03/22/Android-Bitmap%E9%9D%A2%E9%9D%A2%E8%A7%82/]