整理 | 包包
初識
Bitmap是圖像處理的最重要類之一。用它可以獲取圖像文件信息,進(jìn)行圖像顏色變換、剪切、旋轉(zhuǎn)、縮放等操作,并可以指定格式保存圖像文件。
許多 Android 開發(fā)者都對 Bitmap 不陌生,其作為顯示圖片的載體,會經(jīng)常接觸。而在日常開發(fā)中對圖片的處理通常會用到第三方的開源庫:Glide、Fresco、Picasso...,這些已經(jīng)足夠完善的工具不需要讓我們考慮處理 Bitmap 的細(xì)節(jié),這使得我們對其不是那么熟悉。
Bitmap 實(shí)實(shí)在在是內(nèi)存使用的“大客戶”。如何更好的使用 Bitmap,減少其對App內(nèi)存的使用,是 Android 優(yōu)化方面不可回避的問題,因此,本文從常規(guī)的 Bitmap 使用,到 Bitmap 內(nèi)存計算,最后分析如何更有效的使用 Bitmap。
了解
Bitmap 占用了多大的內(nèi)存
Bitmap 用來處理位圖,每一張圖片的每個像素點(diǎn)都會被讀取,每個像素點(diǎn)的大小決定了 Bitmap 的內(nèi)存大小。
所以計算內(nèi)存大小的公式為:
占用的內(nèi)存大小 = 像素總數(shù)量(寬x高)x 每個像素的字節(jié)大小
單個像素的字節(jié)大小
單個像素的字節(jié)大小由Bitmap的一個可配置的參數(shù)Config來決定。Bitmap中,存在一個枚舉類Config,定義了Android中支持的Bitmap配置:
Android系統(tǒng)中,默認(rèn)Bitmap加載圖片,使用ARGB_8888模式。
Bitmap 占用內(nèi)存大小實(shí)例
我們準(zhǔn)備一張分辨率為 1920x1080,大小為 273KB 的 jpg 圖片,放在手機(jī)的 SD 卡中,調(diào)用 BitmatFactory.decodeFile() 加載并顯示到一個大小為 640x320 的 ImageView 中,占用的內(nèi)存如下:
從計算內(nèi)存大小的公式可以得到加載這張圖片使用了大約 7m 的內(nèi)存,即使是手機(jī)內(nèi)存普遍上漲的今天,這樣的開銷也是法接受的。
在剛才的實(shí)例中,我們是將圖片放在了手機(jī)的外置 SD 卡中,現(xiàn)在,我們將圖片分別放到項(xiàng)目工程的 mipmap-xhdpi, mipmap-xxhdpi, mipmap-xxxhdpi 這三個資源目錄中,調(diào)用 BitmatFactory.decodeResource() 加載到同樣的 ImageView 中看看加載的情況:
我們發(fā)現(xiàn),圖片放在不同的資源目錄中、使用不同的方法加載,占用的內(nèi)存也會不同,為了探究這其中原理,需要通過觀察 Bitmap.decode 的源碼,這一過程是有 native 來完成的,所以我們找到 BitmapFactory.cpp#nativeDecode 開始跟蹤,省略了其他不相關(guān)的代碼:
上述代碼中,最終 bitmap 是通過 canvas 繪制出來,而 canvas 繪制前有 scale 的操作 scale = (float) targetDensity / density; 這一行代碼決定,即縮放的倍率和 targetDensity 和 density 相關(guān),而這兩個參數(shù)都是從傳入的 options 中獲取到的,再到 Bitmap.Options 中找到相關(guān)的參數(shù):
? inDensity:Bitmap 位圖自身的密度、分辨率
? inTargetDensity: Bitmap 最終繪制的目標(biāo)位置的分辨率
其中 inDensity 和圖片存放的資源文件的目錄有關(guān),同一張圖片放置在不同目錄下會有不同的值:
通過以上兩個實(shí)例,我們得出了 decodeResource() 和 decodeFile() 的區(qū)別:
?decodeResource 用于讀取Res、Raw等資源,得到的是圖片的原始尺寸 * 縮放系數(shù)(inDensity)
?decodeFile 用于讀取SD卡上的圖,得到的是圖片的原始尺寸
手動設(shè)置縮放系數(shù)
在 Bitmap.Options 中還有一個為 inScaled 的屬性,如果設(shè)置為 false,則不進(jìn)行縮放,如果設(shè)置為 true 或者不設(shè)置,則根據(jù) inDensity 和 inTargetDensity 計算縮放系數(shù)。 如果你不想依賴于這個系統(tǒng)本身的 density,你可以手動設(shè)置 inDensity 和 inTargetDensity 來控制縮放系數(shù):
壓縮方式 inSampleSize & quality
inSampleSize 指的是壓縮分辨率,取值必須為 2 的冪(當(dāng)不為2的冪時,解碼器會取與該值最接近的2的冪),例如,當(dāng) inSampleSize = 2 時,一張 1920x1080 的圖片,將會被縮小為 960x540,相應(yīng)的它的像素數(shù)和內(nèi)存占用都被縮小為原來的 1/4。
quality 正如字面意思指的是圖片品質(zhì),在代碼中對應(yīng)的 api 為:
CompressFormat 為 Bitmap 中的枚舉類,有三個可用值:
? JPEG:表示以 JPEG 壓縮算法進(jìn)行圖像壓縮,壓縮后的格式可以是 “.jpg” 或者 “.jpeg” ,是一種有損壓縮。
? PNG:表示以 PNG 壓縮算法進(jìn)行圖像壓縮,壓縮后的格式可以是 “.png” ,是一種無損壓縮。
? WEBP:表示以 WebP 壓縮算法進(jìn)行圖像壓縮,壓縮后的格式可以是 “.webp” ,是一種有損壓縮,質(zhì)量相同的情況下,WebP 格式圖像的體積要比 JPEG 格式圖像小40%。美中不足的是,WebP格式圖像的編碼時間“比JPEG格式圖像長8倍”。
quality 為圖片的品質(zhì),取值為 0-100,100 代表最高品質(zhì),不被壓縮。另外,類似 PNG 這種無損格式會忽略 quality 的設(shè)置 stream 為圖片被壓縮后被保存在的輸出流。
然而 Bitmap.compress 方法確實(shí)可以壓縮圖片,但壓縮的是存儲大小,即放到 disk 上的大小。
調(diào)試
現(xiàn)在我們通過幾個實(shí)例,來驗(yàn)證一下以上的結(jié)論,首先來看一下兩種壓縮方式占用內(nèi)存的影響:
inSampleSize
顯示結(jié)果 :
以上 ImageView 的大?。?40x320),用來加載 1920x1080 的圖片確實(shí)有些浪費(fèi),所以經(jīng)過計算,將原圖壓縮后發(fā)現(xiàn)圖片占用內(nèi)存的大小減少到原圖的 1/10,如果原圖本身與控件的大小相差不多,這時候還要縮放的話就會影響到圖片顯示的質(zhì)量。
降低圖片品質(zhì)
顯示結(jié)果 :
使用降低圖片質(zhì)量的方式壓縮圖片,可以發(fā)現(xiàn)盡管已經(jīng)降低了 90% 的品質(zhì),圖片也變得模糊,但其占用的內(nèi)存與直接加載還是一樣的。
改變 Bitmap.Config
我們已經(jīng)知道, Bitmap 加載圖片默認(rèn)使用的 config 為 ARGB_8888,而且 ALPHA_8 是只有透明度的, 所以我們來看看改為 ARGB_4444 和 RGB_565 所顯示的結(jié)果,只需要在 decode 的時候傳入設(shè)置好的 options 參數(shù),所以這里直接給出顯示結(jié)果:
可以看到將 config 改為 ARGB_4444,所占用的內(nèi)存與原圖一樣,而 RGB_565,變得是原圖的 1/2,所以結(jié)論也不言而喻了,另外 ARGB_4444,已被官方標(biāo)記為廢棄。
總結(jié)
在上面,我們將一張 1920x1080 的圖片,不做任何處理解析到內(nèi)存中,將近占用的 7M,想象一下這樣的開銷發(fā)生在一個圖片列表中,內(nèi)存占用將達(dá)到非??鋸埖牡夭?。從之前Bitmap占用內(nèi)存的計算公式來看,減少內(nèi)存主要可以通過以下幾種方式:
? 使用低色彩的解析模式,如RGB565,減少單個像素的字節(jié)大小。這樣大約能減少一半的內(nèi)存開銷。Android 默認(rèn)是使用 ARGB_8888 配置來處理色彩,占用4字節(jié),改用RGB_565,將只占用2字節(jié),代價是顯示的色彩將相對少,適用于對色彩豐富程度要求不高的場景。
? 資源文件合理放置,高分辨率圖片可以放到高分辨率目錄下。和圖片的具體分辨率有關(guān),建議開發(fā)中,高分辨率的圖像應(yīng)該放置到合理的資源目錄下,注意到Android默認(rèn)放置的資源目錄是對應(yīng)于160dpi,目前手機(jī)屏幕分辨率越來越高,此處能節(jié)省下來的開銷也是很可觀的。理論上,圖片放置的資源目錄分辨率越高,其占用內(nèi)存會越小,但是低分辨率圖片會因此被拉伸,顯示上出現(xiàn)失真。另一方面,高分辨率圖片也意味著其占用的本地儲存也變大。
? 圖片縮小,減少尺寸。理論上根據(jù)適用的環(huán)境,是可以減少十幾倍的內(nèi)存使用的,它基于這樣一個事實(shí):源圖片尺寸一般都大于目標(biāo)需要顯示的尺寸,因此可以通過縮放的方式,來減少顯示時的圖片寬高,從而大大減少占用的內(nèi)存。
-
Android
+關(guān)注
關(guān)注
12文章
3935瀏覽量
127339 -
BITMAP
+關(guān)注
關(guān)注
0文章
4瀏覽量
6375
發(fā)布評論請先 登錄
相關(guān)推薦
評論