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