Android內(nèi)存優(yōu)化一:java垃圾回收機制
Android內(nèi)存優(yōu)化二:內(nèi)存泄漏
Android內(nèi)存優(yōu)化三:內(nèi)存泄漏檢測與監(jiān)控
Android內(nèi)存優(yōu)化四:OOM
Android內(nèi)存優(yōu)化五:Bitmap優(yōu)化
一、占用內(nèi)存計算
1.從本地加載或者從網(wǎng)絡加載
// 1個字節(jié)8位
內(nèi)存占用 = 圖片寬 * 高 * 一個像素點占用的字節(jié)數(shù)
2.從資源目錄記載数冬, 圖片會被壓縮
壓縮比:scale = (flaot) targetDensity / density
targetDensity : 設備屏幕像素密度dpi
density: 圖片對應的文件夾的像素密度dpi
1)帽撑、同一張圖片放在不同的資源目錄下,其分辨率會有變化。
2)甥温、Bitmap的分辨率越高调鬓,其解析后的寬高越小,甚至小于原有的圖片(及縮放)耸成,從而內(nèi)存也響應的減少。
3)浴鸿、圖片不放置任何資源目錄時井氢,其使用默認分辨率mdpi:160。
4)赚楚、資源目錄分辨率和屏幕分辨率一致時毙沾,圖片尺寸不會縮放。
Bitmap放在資源目錄中的計算方式為:
內(nèi)存占用 = 像素數(shù)據(jù)總大小 = 圖片寬 * 圖片高 * scale ^ 2 * 一個像素點占用的字節(jié)數(shù)
二宠页、圖片內(nèi)存優(yōu)化
主要通過編碼左胞、采樣、復用举户、匿名共享區(qū)進行優(yōu)化
編碼
由于ARGB_4444的畫質(zhì)慘不忍睹烤宙,一般假如對圖片沒有透明度要求的話,可以改成RGB_565俭嘁,相比ARGB_8888將節(jié)省一半的內(nèi)存開銷
其中躺枕,A代表透明度;R代表紅色;G代表綠色拐云;B代表藍色罢猪。
ALPHA_8 表示8位Alpha位圖,即A=8,一個像素點占用1個字節(jié),它沒有顏色,只有透明度。
ARGB_4444 表示16位ARGB位圖叉瘩,即A=4,R=4,G=4,B=4,一個像素點占4+4+4+4=16位膳帕,2個字節(jié)。
ARGB_8888 表示32位ARGB位圖薇缅,即A=8,R=8,G=8,B=8,一個像素點占8+8+8+8=32位危彩,4個字節(jié)。
RGB_565 表示16位RGB位圖,即R=5,G=6,B=5,它沒有透明度,一個像素點占5+6+5=16位泳桦,2個字節(jié)汤徽。
采樣
bitmap的占用內(nèi)存,是以bitmap的寬高和每個像素占用的字節(jié)數(shù)決定的灸撰。
BitmapFactory.Options options = new BitmapFactory.Options();
//不獲取圖片谒府,不加載到內(nèi)存中,只返回圖片屬性
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(photoPath, options);
//圖片的寬高
int outHeight = options.outHeight;
int outWidth = options.outWidth;
Log.d("bitmap", "圖片寬=" + outWidth + "圖片高=" + outHeight);
//計算采樣率
int i = utils.computeSampleSize(options, -1, 1000 * 1000);
//設置采樣率梧奢,不能小于1 假如是2 則寬為之前的1/2狱掂,高為之前的1/2演痒,一共縮小1/4 以此類推
options.inSampleSize = i;
Log.d("bitmap", "采樣率為=" + i);
//圖片格式壓縮
//options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(photoPath, options);
float bitmapsize = getBitmapsize(bitmap);
Log.d("bitmap","壓縮后:圖片占內(nèi)存大小" + bitmapsize + "MB / 寬度=" + bitmap.getWidth() + "高度=" + bitmap.getHeight());
// 打印
bitmap: 原圖:圖片占內(nèi)存大小=45.776367MB / 寬度=4000高度=3000
bitmap: 圖片寬=4000圖片高=3000
bitmap: 采樣率為=4
bitmap: 壓縮后:圖片占內(nèi)存大小1.4296875MB / 寬度=1000高度=750
根據(jù)BitmapFactory 的采樣率進行壓縮 設置采樣率亲轨,不能小于1 假如是2 則寬為之前的1/2,高為之前的1/2鸟顺,一共縮小1/4 以此類推
復用
圖片復用指的是inBitmap這個屬性惦蚊。
不使用這個屬性,你加載三張圖片讯嫂,系統(tǒng)會給你分配三份內(nèi)存空間蹦锋,用于分別儲存這三張圖片
如果用了inBitmap這個屬性,加載三張圖片欧芽,這三張圖片會指向同一塊內(nèi)存莉掂,而不用開辟三塊內(nèi)存空間。
inBitmap的限制:
1千扔、3.0-4.3
復用的圖片大小必須相同
編碼必須相同
2憎妙、4.4以上
復用的空間大于等于即可
編碼不必相同
3、不支持WebP
4曲楚、圖片復用厘唾,這個屬性必須設置為true;
options.inMutable = true;
匿名共享內(nèi)存
Android 系統(tǒng)為了進程間共享數(shù)據(jù)開辟的一塊內(nèi)存區(qū)域龙誊,由于這塊區(qū)域不受應用的Head的大小限制抚垃,相當于可以繞開oom,F(xiàn)aceBook的Fresco首次應用到實際中。
限制:5.0以后就限制了匿名共享內(nèi)存的使用鹤树。
Android的內(nèi)存區(qū)域
Java Heap(Dalvik Heap)铣焊,這部分的內(nèi)存區(qū)域是由Dalvik虛擬機管理,通過Java中 new 關鍵字來申請一塊新內(nèi)存罕伯。這塊區(qū)域的內(nèi)存是由GC直接管理粗截,能夠自動回收內(nèi)存。這塊內(nèi)存的大小會受到系統(tǒng)限制捣炬,當內(nèi)存超過APP最大可用內(nèi)存時會OOM
Native Heap熊昌,這部分內(nèi)存區(qū)域是在C++中申請的,它不受限于APP的最大可用內(nèi)存限制湿酸,而只是受限于設備的物理可用內(nèi)存限制婿屹。它的缺點在于沒有自動回收機制,只能通過C++語法來釋放申請的內(nèi)存
Ashmem(Android匿名共享內(nèi)存)推溃,這部分內(nèi)存類似于Native內(nèi)存區(qū)昂利,但是它是受Android系統(tǒng)底層管理的,當Android系統(tǒng)內(nèi)存不足時铁坎,會回收Ashmem區(qū)域中狀態(tài)是 unpin 的對象內(nèi)存塊蜂奸,如果不希望對象被回收,可以通過 pin 來保護一個對象
使用 inBitmap 需要注意幾個限制條件
在SDK 11 -> 18之間硬萍,重用的bitmap大小必須是一致的扩所,例如給inBitmap賦值的圖片大小為100-100,那么新申請的bitmap必須也為100-100才能夠被重用朴乖。從SDK 19開始祖屏,新申請的bitmap大小必須小于或者等于已經(jīng)賦值過的bitmap大小。 新申請的bitmap與舊的bitmap必須有相同的解碼格式买羞,例如大家都是8888的袁勺,如果前面的bitmap是8888,那么就不能支持4444與565格式的bitmap了畜普。 我們可以創(chuàng)建一個包含多種典型可重用bitmap的對象池期丰,這樣后續(xù)的bitmap創(chuàng)建都能夠找到合適的“模板”去進行重用。
圖片到底儲存在哪里
8.0Bitmap的像素數(shù)據(jù)存儲在Native吃挑,為什么又改為Native存儲呢钝荡?
因為8.0共享了整個系統(tǒng)的內(nèi)存,測試8.0手機如果一直創(chuàng)建Bitmap儒鹿,如果手機內(nèi)存有1G化撕,那么你的應用加載1G也不會oom。
LRU管理Bitmap
可以利用LRU開管理Bitmap约炎,給他設置內(nèi)存最大值植阴,及時回收蟹瘾。
圖片壓縮
- 采樣壓縮(設置采樣率,不能小于1 假如是2 則寬為之前的1/2掠手,高為之前的1/2憾朴,一共縮小1/4 以此類推)
- 質(zhì)量壓縮
// 這個壓縮是保持像素的前提下改變圖片的位深及透明度,來達到壓縮的目的喷鸽,
// 不過這種壓縮不會改變圖片在內(nèi)存中的大小众雷,而且這種壓縮會導致圖片的失真,
// 但是有沒有壓縮到100k左右做祝,還不失真的方法砾省?
bitmap.compress(Bitmap.CompressFormat.JPEG, 20,
new FileOutputStream("sdcard/result.jpg"));
加載高清圖
BitmapRegionDecoder
//支持傳入圖片的路徑,流和圖片修飾符等
BitmapRegionDecoder mDecoder = BitmapRegionDecoder.newInstance(path, false);
//需要顯示的區(qū)域就有由rect控制混槐,options來控制圖片的屬性
Bitmap bitmap = mDecoder.decodeRegion(mRect, options);