由于Android對(duì)單個(gè)應(yīng)用所施加的內(nèi)存限制,比如16MB,這導(dǎo)致加載Bitmap的時(shí)候很容易出現(xiàn)內(nèi)存溢出,本文主要包含2個(gè)方面的內(nèi)容分析Bitmap內(nèi)存和Bitmap高效加載
站在用戶的角度思考問題,與客戶深入溝通,找到蓮湖網(wǎng)站設(shè)計(jì)與蓮湖網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、申請(qǐng)域名、雅安服務(wù)器托管、企業(yè)郵箱。業(yè)務(wù)覆蓋蓮湖地區(qū)。
一、占用內(nèi)存
獲取bitmap的內(nèi)存,android提供的方法bitmap.getByteCount()
假如現(xiàn)在mipmap-xhdpi 目錄下,有一個(gè) 200 * 200 像素的圖片,運(yùn)行加載它,看它輸出的尺寸。
Bitmap bitmap= BitmapFactory.decodeResource(getResources(),R.mipmap.btn_go);
bitmap.getByteCount()的輸出結(jié)果為360000
現(xiàn)在把圖片轉(zhuǎn)移到mipmap-xxhdpi 中
bitmap.getByteCount的輸出結(jié)果為160000
那么mipmap-xhdpi 目錄下為何會(huì)大這么多呢?
density 影響 Bitmap 內(nèi)存
放在不同的 mipmap 目錄下,對(duì)應(yīng)的不同 density 的設(shè)備。density 是設(shè)備的固有參數(shù),伴隨著 density 的,還有 densityDpi,它也是與設(shè)備相關(guān)的,表示屏幕每英寸對(duì)應(yīng)多少個(gè)點(diǎn)(非像素點(diǎn))
上面是官方提供的一張比較經(jīng)典的圖,可以看到,不同的目錄,代表不同的 density ,例如 xhdpi 代表的 density 就是 2。而這里的 density 對(duì) densityDip 的基準(zhǔn)是 160 ,也就是說,mdpi 對(duì)應(yīng)的 densityDpi 是 160 ,xhdpi 對(duì)應(yīng)的 densityDpi 是 320,同樣xxhdpi對(duì)應(yīng)的densityDpi是480
density 和 densityDpi 在 Android 中,都有標(biāo)準(zhǔn)的 API 可以拿到,如下。
DisplayMetrics displayMetrics=new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); Log.i(TAG, displayMetrics.densityDpi+""); Log.i(TAG,displayMetrics.density+"");
這是我手機(jī)的輸出結(jié)果
densityDpi:480 density:3.0
從getByteCount()源碼得知,Bitmap.getByteCount() 是由:
(bitmapWidth * scale) * (bitmapHeight * scale) *(每像素所占字節(jié)大小)得出來的
其中:int scale = phoneDensity / inDensity
phoneDensity是我們手機(jī)的densityDpi,上述得出我手機(jī)是480
inDensity是你圖片存放的路徑,比如xhdpi是320,xxhdpi是480
BitmapFactory默認(rèn)色彩度為 ARGB_8888 圖片,每像素會(huì)占用 4 bytes
在xhdpi下:200 * (480/320) * 200(480/320)4= 360000
在xxhdpi下:200 * (480/480) * 200(480/480)4= 160000
以上就是bitmap所占內(nèi)存分析。
二、高效加載
1.修改bitmap.config
2.修改inSampleSize
bitmap.config簡介
上面提到BitmapFactory默認(rèn)色彩度為 ARGB_8888
Bitmap.Config一共有四個(gè)參數(shù)如下:
(這些參數(shù)決定了Bitmap位圖的配置,會(huì)影響到bitmap的像素如何、色彩、以及是否有透明度的能力)
Bitmap.Config ALPHA_8
這個(gè)參數(shù)每個(gè)像素占用1字節(jié)的空間。
它代表每個(gè)像素點(diǎn)被存儲(chǔ)為單個(gè)透明度的通道,這對(duì)于設(shè)置遮罩的圖片用例十分有用,它不存儲(chǔ)顏色信息。
Bitmap.Config RGB_565
這個(gè)參數(shù)每個(gè)像素占用2字節(jié)的空間。
它代表只有RGB通道的編碼,其中紅色占用5位地址,綠色占用6位地址,藍(lán)色占用5位地址。沒有透明度的通道。
使用不透明的位圖時(shí),不要求高的色彩保真度使用此配置是不錯(cuò)的選擇。
Bitmap.Config ARGB_4444
這個(gè)參數(shù)每個(gè)像素占用2字節(jié)的空間。
它一共有四個(gè)通道,顧名思義,分別是透明度、紅、綠、藍(lán)。每個(gè)通道分別占用四位地址,所以一共2字節(jié)。
當(dāng)應(yīng)用需要節(jié)省內(nèi)存(對(duì)色彩質(zhì)量要求低),同時(shí)又需要存儲(chǔ)透明度信息,這個(gè)配置可以作為選擇,但官方比較推薦用ARGB_8888的設(shè)置,因?yàn)檫@個(gè)的色彩質(zhì)量差。
Bitmap.Config ARGB_8888
這個(gè)參數(shù)每個(gè)像素占用4字節(jié)的空間。
這也是一共4個(gè)通道,但不一樣的是每個(gè)通道站8位地址,因而色彩質(zhì)量比上一個(gè)設(shè)置高了特別特別多(16倍)。
能夠滿足最好的位圖質(zhì)量,在內(nèi)存充足的情況下,十分推薦使用這個(gè)。
inSampleSize簡介
通過BitmapFactory.Options來縮放圖片,主要是用inSampleSize參數(shù),當(dāng)inSampleSize=1時(shí),采樣后的圖片為圖片的原始大小,當(dāng)inSampleSize=2時(shí),采樣后的圖片寬,高均為原圖大小的1/2,而像素?cái)?shù)為原圖的1/4,假定圖片原有的內(nèi)存是4MB,如果把它的inSampleSize設(shè)為2,它的內(nèi)存就會(huì)變成1MB
具體實(shí)現(xiàn)代碼如下
public static Bitmap decodeBitmapFromResource(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=1; options.inSampleSize=setInSampleSize(options,reqWidth,reqHeight); options.inPreferredConfig= Bitmap.Config.ARGB_4444; options.inJustDecodeBounds=false; return BitmapFactory.decodeResource(res,resId,options); } public static int setInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){ final int height=options.outHeight; final int width=options.outWidth; int inSampleSize=1; if(height>reqHeight||width>reqHeight){ final int halfHeght=height/2; final int halfWidht=width/2; while ((halfHeght/inSampleSize>=reqHeight)&&(halfWidht/inSampleSize>=reqWidth)){ inSampleSize *=2; } } return inSampleSize; }
有了上面兩個(gè)方法,實(shí)際使用就簡單了,比如imageView所期望的圖片大小為100X100,這個(gè)時(shí)候我們就可以這樣調(diào)用,還是之前存放在xxhdpi中的圖片,上述代碼中已經(jīng)把bitmap.config設(shè)置成ARGB_4444 ,現(xiàn)在把原先尺寸200X200改成100X100,看下內(nèi)存是多少
Bitmap bitmap= decodeBitmapFromResource(getResources(),R.mipmap.btn_go,100,100); Log.i(TAG,bitmap.getByteCount()+"");
bitmap.getByteCount的輸出結(jié)果為20000,比之前160000減少了8倍, ARGB_4444較ARGB_8888減少了2倍,設(shè)置尺寸100,100傳入setInSampleSize()方法中得到inSampleSize=2, 像素?cái)?shù)為原圖的1/4,內(nèi)存大小總共就變成了之前的1/8,這樣高效加載圖片,就會(huì)遠(yuǎn)離oom。