android圖片內存計算
A. Android 高效內存-圖片內存使用優化
內容整理自網路。
在做內存優化的時候,我們發現除了解決內存泄露問題,剩下的就只有想辦法減少真實的內存佔用。而在App中,大部分內存可能被我們圖片佔用了,所以減少圖片的內存佔用可以帶來直接的效果。本文就簡單介紹一張圖片到底佔用多少內存,我們先假設我們有一張圖片時** 600 * 800** 的,圖片佔用空間大小假設是** 100KB**。
圖片內存大小跟佔用空間大小有什麼關系?
佔用空間的大小不是圖片佔用內存的大小,一些初學者可能會誤解一下。佔用空間是在磁碟上佔用的空間,內存大小是載入到內存中佔用的內存大小。兩個只是單位是一樣的,本質不是一個概念。
一張圖片到底佔用多少內存呢?(ARGB_8888編碼)
1. 圖片佔用內存的計算公式: 圖片高度 * 圖片寬度 * 一個像素佔用的內存大小
2. 所以上面的圖片佔用內存是:**800 * 600 * 4 byte = 1875KB = 1.83M **
上面的計算公式中,為什麼是4byte呢?文章後面有總結哦
圖片所在目錄對內存的影響?
在Android中,圖片的存放目錄和手機的屏幕密度影響圖片最終的大小,舉個例子:
假設我們的圖片放到 xhdpi 目錄下,那麼我們本文中的圖片佔用的內存大小如下:
屏幕密度為2的設備:800 * 600 * 4byte = 1.83M
屏幕密度為3的設備:800 * 1.5 * 600 * 1.5 * 4byte = 1.83 * 2.25M =** 4.12M**
所以,計算圖片佔用內存大小的時候,要考慮圖片所在的目錄跟設備密度,這兩個因素其實影響的是圖片的高寬,android會對圖片進行拉升跟壓縮。
總結
1. 圖片確實很佔用內存,內存優化先考慮圖片內存佔用;
2. 一定要避免使用大圖片,這就是.9圖很有用的原因之一;
3. 圖片的大小對內存的影響是正比關系;
4. 本文只是簡單的告知讀者怎麼計算圖片的內存大小。
大圖: 440 * 336 小圖: 220 * 168 資源目錄: xhdpi
小圖的高寬都是大圖的1/2-->小圖是原圖的1/4
界面效果:
測試設備: Coolpad 8676-M01 5.1 density=2.0
測試前准備操作: 同一款設備,設置圖片前後多次調用gc直到內存短時間內保持穩定不再變化
內存使用情況: 下圖依次是 初始內存,大圖內存,小圖內存
大圖佔用內存: 11.23 MB - 10.66 MB = 0.57 MB
小圖佔用內存: 10.81 MB - 10.66 MB = 0.15 MB
大圖小圖內存關系: 0.15 MB * 4 = 0.60 MB 約等於 0.57 MB (這是統計工具的誤差,理論上就是相等的)
同樣的方式在另外一台設備小米4c上得到的結果如下:
測試設備: Xiaomi Mi-4c V8.2.1.0.LXKCNDL 5.1.1 density=3.0
大圖佔用內存: 13.22 MB - 11.95 MB = 1.27 MB
小圖佔用內存: 12.27 MB - 11.95 MB = 0.32 MB
大圖小圖內存關系: 0.32 MB * 4 = 1.28 MB 約等於 1.27 MB
結論: 由此可見大圖比小圖佔用更多的內存,圖片大小(解析度)與佔用內存成正比關系
備註: 圖片在硬碟上佔用的磁碟空間大小,與在內存中佔用的內存大小完全不一樣,不是一個概念,不要混淆
根據上文中圖片大小與內存的關系,可以更加深刻的理解Android中.9圖片的作用,它不但能減少apk的體積,還能減少圖片佔用內存。
有些時候我們根本不需要圖片,而是自己繪制背景,可以在自定義View的onDraw中繪制背景,當然最方便的還是使用系統的Drawable,繪制部分交給系統去完成。
下面測試圖片與Drawable的內存佔用對比
原始圖片大小: 482 * 482
界面效果:
測試設備: Xiaomi Mi-4c V8.2.1.0.LXKCNDL 5.1.1
測試前准備操作: 同一款設備,設置背景前後多次調用gc直到內存短時間內保持穩定不再變化
內存使用情況: 下圖依次是 初始內存,使用圖片佔用的內存,使用Drawable佔用的內存,使用onDraw繪制佔用的內存
使用圖片佔用內存: 13.97 MB - 11.97 MB = 2.00 MB
使用Drawable佔用內存: 11.97 MB - 11.97 MB = 0.00 MB (不會是0,有誤差,只是很少)
使用onDraw繪制佔用內存: 11.98 MB - 11.97 MB = 0.01 MB
結論: 繪制背景,或者使用系統提供Drawable作為背景,會大大減少內存佔用
Drawable參考資料:
Drawable實戰解析:Android XML shape 標簽使用詳解(apk瘦身,減少內存好幫手)
Android GradientDrawable(shape標簽定義)靜態使用和動態使用(圓角,漸變實現)
「讓你的圖片最小化」一節中描述的方法:使用盡可能小的圖,使用.9,自己繪制背景或者使用Drawable來繪制背景
載入大圖片時需要對圖片進行壓縮,使用等比例壓縮方法直接在內存中處理圖片
這樣做要注意的是,圖片質量會變差,inSampleSize設置的值越大,圖片質量就越差。
有時候我們取得一張圖片,也許只是為了獲得這個圖片的一些信息,比如圖片的width、height等信息,不需要顯示到界面上,這個時候我們可以不把圖片載入到內存中。
由於Android外層是使用java,而底層使用的是C語言為圖片對象分配的內存空間。所以我們的外部雖然看起來釋放了,但里層卻並不一定完全釋放了,我們使用完圖片後最好再釋放掉里層的內存空間。
RGB(ARGB)
RGB色彩模式是工業界的一種顏色標准,是通過對紅(R)、綠(G)、藍(B)三個顏色通道的變化以及它們相互之間的疊加來得到各式各樣的顏色的,RGB即是代表紅、綠、藍三個通道的顏色,這個標准幾乎包括了人類視力所能感知的所有顏色,是目前運用最廣的顏色系統之一。在Android中還有包含透明度Alpha的顏色模型,即ARGB。
YUV
YUV,分為三個分量,「Y」表示明亮度(Luminance或Luma),也就是灰度值;而「U」和「V」 表示的則是色度(Chrominance或Chroma),作用是描述影像色彩及飽和度,用於指定像素的顏色。
YUV的原理是把亮度與色度分離,研究證明,人眼對亮度的敏感超過色度。利用這個原理,可以把色度信息減少一點,人眼也無法查覺這一點。
主要用於電視系統以及模擬視頻領域,它將亮度信息(Y)與色彩信息(UV)分離,沒有UV信息一樣可以顯示完整的圖像,只不過是黑白的,這樣的設計很好地解決了彩色電視機與黑白電視的兼容問題
YUV的存儲中與RGB格式最大不同在於,RGB格式每個點的數據是連繼保存在一起的。即R,G,B是前後不間隔的保存在2-4byte空間中。而YUV的數據中為了節約空間,U,V分量空間會減小。每一個點的Y分量獨立保存,但連續幾個點的U,V分量是保存在一起的,(反正人眼一般也看不出區別).這幾個點合起來稱為macro-pixel, 這種存儲格式稱為Packed格式。另外一種存儲格式是把一幅圖像中Y,U,V分別用三個獨立的數組表示。這種模式稱為planar模式。
CMYK
CMYK也稱作印刷色彩模式,顧名思義就是用來印刷的。印刷四分色模式是彩色印刷時採用的一種套色模式,利用色料的三原色混色原理,加上黑色油墨,共計四種顏色混合疊加,形成所謂「全彩印刷」。四種標准顏色是:
CMYK和RGB相比有一個很大的不同:RGB模式是一種發光的色彩模式,你在一間黑暗的房間內仍然可以看見屏幕上的內容;CMYK是一種依靠反光的色彩模式,我們是怎樣閱讀報紙的內容呢?是由陽光或燈光照射到報紙上,再反射到我們的眼中,才看到內容。它需要有外界光源,如果你在黑暗房間內是無法閱讀報紙的。只要是在印刷品上看到的圖像,就是CMYK模式表現的。比如期刊、雜志、報紙、宣傳畫等,都是印刷出來的,那麼就是CMYK模式的了。
CMYK原色與疊加之後的顏色對比
在不考慮透明度的情況下,一個像素點的顏色值在計算機中的表示方法有以下3種:
在Java中,float類型的變數佔32位,int類型的變數佔32位,short和char類型的變數都在16位,因此可以看出,用浮點數表示法編碼一個像素的顏色,內存佔用量是96位即12位元組;而用24位整數表示法編碼,只要一個int類型變數,佔用4個位元組(高8位空著,低24位用於表示顏色);用16位整數表示法編碼,只要一個short類型變數,佔2個位元組;因此可以看出採用整數表示法編碼顏色值,可以大大節省內存,當然,顏色質量也會相對低一些。在Android中獲取Bitmap的時候一般也採用整型編碼。
回想一下Android的BitmapConfig類中,有ARGB_8888、ARGB_4444、RGB565等常量,現在可以知道它們分別代表了什麼含義。同時也可以計算一張圖片在內存中可能佔用的大小,比如採用ARGB_8888編碼載入一張1920 1200的圖片,大概就會佔用1920 1200*4/1024/1024=8.79MB的內存。
採用低內存佔用量的編碼方式,比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省內存;
1920 1200的圖片:*
ARGB_8888:1920 1200 4/1024/1024=8.79MB
ARGB_4444,RGB565:1920 1200 2/1024/1024=4.39MB
在Android中,對圖片的使用一定要關注,大多數情況下,佔用內存多,OOM發生都是因為圖片資源使用不當。不要盲目加一個大圖到Android項目中,能使用.9進來使用,而且.9圖本身盡可能小,另外能使用繪制實現就不要加一個圖片資源。有些時候,在不影響用戶體驗的情況下,可以降低圖片素材質量,比如不需要透明度的就不要了,有些透明度用肉眼看不出來。
B. Android 適配一篇就夠 - 編譯版本supportAPI 兼容圖片適配
本文介紹 Android 不同系統及圖片資源的常見適配問題。
基本原則:先查找和屏幕密度最匹配的目錄,如果沒有,則依次向高密度目錄查找,如果查到最高也沒有,則查找 drawable-nodpi 目錄(該目錄無論設備密度如何,系統都不會縮放此目錄中的資源),如果還是沒有,再依次像低密度目錄查找;
我們以具體的實例分析該問題。實例:一個本該放在 hdpi(對應設備 dpi:240) 的圖片,被放在了 xxhdpi(對應設備 dpi:480) 目錄,圖片還是在 dpi 為 240 的設備上顯示時,大小是放大還是縮小?放大或縮小幾倍?內存佔用是增大還是減小?增大或者減小幾倍?
首先要了解圖片內存佔用計算公式: 內存大小 = 解析度 x 每個像素點大小 ,其中解析度是圖片載入到內存後像素點的總個數(寬度 x 高度),每個像素的大小取決於使用的數據格式,如 ARGB_8888 格式就會佔用 4 個位元組。這里特別說明了,解析度是圖片載入到內存後的像素點個數,並不是載入前的像素數,二者關系為:載入後寬度/高度 = 載入前寬度/高度 x (設備 dpi / 圖片所在 drawable 的dpi)。因此答案也就一目瞭然了,新的寬高都減少了1/2,因此 總內存大小減小了1/4 。
C. Android性能優化(八)--Android圖片內存優化
2個基本原則
既然需要的內存公式已得到,那優化就顯而易見了,無非就是減小的這三個參數的值,具體的策略如下:
這里我們將圖片分為2種情況來探討:
圖片佔用的內存 大小為:
為什麼mipmap不在這種情況的考慮范圍之內呢?
因為mipmap是Android系統為了避免Launcher Icon變形而添加的資源目錄,也就是說,mipmap中的圖片不會被縮放。所以Google也不推薦將除Launcher Icon之外的圖片放在mipmap目錄中。
本地圖片通常都是通過Android提供的BitmapFactory來載入的, 這里看幾個常用的API:
圖片的優化可通過Options參數來實現(Options的介紹可參考 從fresco 看圖片優化 :
inPreferredConfig的取值為Bitmap.Config類型(這里只考慮以下幾種情況),它是一個枚舉類型,用來設置每個像素需要的位元組數:
1.jpeg和gif
2.webp
3.png8, png24, png32
網路圖片通常我們都是使用開源庫進行載入, 所以不需要拿到Bitmap再進行縮放或裁剪。
這時可讓後台實現網路圖片的裁剪,即:根據圖片的請求參數返回合適的尺寸,最大也只需要控制項的大小即可。
再大也沒意義,不僅浪費流量,還佔用內存。
如果你的APP中有很多圖片,那麼可對圖片的寬高根據設備的內存情況進行適當的縮小:
盡量為所有解析度創建資源 資源匹配解析度 = 減少不必要的縮放,從而提高UI繪制效率
對於一個多圖片的APP來說,圖片所佔內存的優化是一項必不可少的工作。
總的來說,其優化也就是通過 縮放 和指定 Bitmap.Config的值 來實現的,只是不同位置,不同格式的圖片有所差異而已。
https://juejin.im/post/5af84f4b51882542714fdaa9?utm_medium=an&utm_source=weixinqun
D. Android-Bitmap復用時內存大小計算
主要是Bitmap的這兩個方法得到的Bitmap的值,會在內存復用體現。
如果不使用內存復用,這兩個方法是一樣的效果。
在通過復用 Bitmap 來解碼圖片時,那麼 getByteCount() 表示新解碼圖片佔用內存的大 小,getAllocationByteCount() 表示被復用 Bitmap真實佔用的內存大小。
比如:上面的圖片,黑色區域是當前Bitmap對象實際內存佔用大小,但是這部分內存中,只有紅色區域是新佔用的,而其他的區域是可以復用的但是沒被復用(即Bitmap中新的圖片只是佔用了8個大小,但是Bitmap實際大小其實是有20),那麼如果使用getByteCount()就只會返回被復用區域的內存大小,所以使用getByteCount()返回內存區域的大小,其實是小於等於實際大小的。
以為你Bitmap佔用內存大小,是由最大的圖片來決定的,如果放入一張更小的圖片,其實並不會減少Bitmap佔用的內存大小。
可以認為:
getByteCount()只是圖片的大小
getAllocationByteCount()是Bitmap的大小
因為Bitmap可以復用,所以Bitmap可以放入不同的圖片,當Bitmap放入更大的圖片的時候,就會佔用更大的內存,但是這個時候如果對Bitmap對象進行復用,放入一張小圖片,並不會改變Bitmap的大小。