非常全面的 Android Bitmap 知識點梳理
阿新 • • 發佈:2018-12-27
在日常開發中,可以說和Bitmap低頭不見擡頭見,基本上每個應用都會直接或間接的用到,而這裡面又涉及到大量的相關知識。
所以這裡把Bitmap的常用知識做個梳理,限於經驗和能力,不做太深入的分析。
1
區別decodeResource()和decodeFile()
這裡的區別不是指方法名和引數的區別,而是對於解碼後圖片尺寸在處理上的區別:
decodeFile()用於讀取SD卡上的圖,得到的是圖片的原始尺寸decodeResource()用於讀取Res、Raw等資源,得到的是圖片的原始尺寸 * 縮放係數
可以看的出來,decodeResource()比decodeFile()多了一個縮放係數,縮放係數的計算依賴於螢幕密度,當然這個引數也是可以調整的:
程式碼1.png
我們分具體情況來看,現在有一張720×720的圖片:
inScaled屬性
如果inScaled設定為false,則不進行縮放,解碼後圖片大小為720×720; 否則請往下看。
如果inScaled設定為true或者不設定,則根據inDensity和inTargetDensity計算縮放係數。
預設情況
把這張圖片放到drawable目錄下, 預設:
以720p的紅米3為例子,縮放係數 = inTargetDensity(具體320 / inDensity(預設160)= 2 = density,解碼後圖片大小為1440×1440。
以1080p的MX4為例子,縮放係數 = inTargetDensity(具體480 / inDensity(預設160)= 3 = density, 解碼後圖片大小為2160×2160。
*dpi資料夾的影響
把圖片放到drawable或者raw這樣不帶dpi的資料夾,會按照上面的演算法計算。
如果放到xhdpi會怎樣呢? 在MX4上,放到xhdpi,解碼後圖片大小為1080 x 1080。
因為放到有dpi的資料夾,會影響到inDensity的預設值,放到xhdpi為160 x 2 = 320; 所以縮放係數 = 480(螢幕) / 320 (xhdpi) = 1.5; 所以得到的圖片大小為1080 x 1080。
手動設定縮放係數
如果你不想依賴於這個系統本身的density,你可以手動設定inDensity和inTargetDensity來控制縮放係數:
程式碼2.png
2
recycle()方法
官方說法
首先,Android對Bitmap記憶體(畫素資料)的分配區域在不同版本上是有區分的:
As of Android 3.0 (API level 11), the pixel data is stored on the Dalvik heap along with the associated bitmap.
從3.0開始,Bitmap畫素資料和Bitmap物件一起存放在Dalvik堆中,而在3.0之前,Bitmap畫素資料存放在Native記憶體中。
所以,在3.0之前,Bitmap畫素資料在Nativie記憶體的釋放是不確定的,容易記憶體溢位而Crash,官方強烈建議呼叫recycle()(當然是在確定不需要的時候);而在3.0之後,則無此要求。
一點討論
3.0之後官方無recycle()建議,是不是就真的不需要recycle()了呢?
在醫生的這篇文章:Bitmap.recycle引發的血案 最後指出:“在不相容Android2.3的情況下,別在使用recycle方法來管理Bitmap了,那是GC的事!”。文章開頭指出了原因在於recycle()方法的註釋說明:
程式碼3.png
事實上這個說法是不準確的,是不能作為recycle()方法不呼叫的依據的。
因為從commit history中看,這行註釋早在08年初始化程式碼的就有了,但是早期的程式碼並沒有因此不需要recycle()方法了。
如果3.0之後真的完全不需要主動recycle(),最新的AOSP原始碼應該有相應體現,我查了SystemUI和Gallery2的程式碼,並沒有取締Bitmap的recycle()方法。
所以,我個人認為,如果Bitmap真的不用了,recycle一下又有何妨?
PS:至於醫生說的那個bug,顯然是一種優化策略,APP開發中加個兩個bitmap不相等的判斷條件即可。
3
inBitmap
BitmapFactory.Options.inBitmap是AndroiD3.0新增的一個屬性,如果設定了這個屬性則會重用這個Bitmap的記憶體從而提升效能。
但是這個重用是有條件的,在Android4.4之前只能重用相同大小的Bitmap,Android4.4+則只要比重用Bitmap小即可。
在官方網站有詳細介紹,這裡列舉示例程式碼的兩個方法瞭解一下:
程式碼4.png
4
LRU快取演算法
LRU,Least Recently Used,Discards the least recently used items first。
在最近使用的資料中,丟棄使用最少的資料。與之相反的還有一個MRU,丟棄使用最多的資料。
這就是著名的區域性性原理。
實現思路
1.新資料插入到連結串列頭部;
2.每當快取命中(即快取資料被訪問),則將資料移到連結串列頭部;
3.當連結串列滿的時候,將連結串列尾部的資料丟棄。
LruCache
在Android3.1和support v4中均提供了Lru演算法的實現類LruCache。
內部使用LinkedHashMap實現。
DiskLruCache
LruCache的所有物件和資料都是在記憶體中(或者說LinkedHashMap中),而DiskLruCache是磁碟快取,不過它的實現要稍微複雜一點。
使用DiskLruCache後就不用擔心檔案或者圖片太多佔用過多磁碟空間,它能把那些不常用的圖片自動清理掉。
DiskLruCache系統中並沒有正式提供,需要另外下載。
5
計算inSampleSize
使用Bitmap節省記憶體最重要的技巧就是載入合適大小的Bitmap,因為以現在相機畫素,很多照片都巨無霸的大,這些大圖直接載入到記憶體,最容易OOM。
載入合適的Bitmap需要先讀取Bitmap的原始大小,按縮小了合適的倍數的大小進行載入。
那麼,這個縮小的倍數的計算就是inSampleSize的計算。
程式碼5.png
關於inSampleSize需要注意,它只能是2的次方,否則它會取最接近2的次方的值。
6
縮圖
為了節省記憶體,需要先設定BitmapFactory.Options的inJustDecodeBounds為true,這樣的Bitmap可以藉助decodeFile方法把高和寬存放到Bitmap.Options中,但是記憶體佔用為空(不會真正的載入圖片)。
有了具備高寬資訊的Options,結合上面的inSampleSize演算法算出縮小的倍數,我們就能載入本地大圖的某個合適大小的縮圖了
程式碼6.png
系統內建了一個ThumbnailUtils也能生成縮圖,細節上不一樣但原理是相同的。
7
Matrix變形
學過線性代數或者影象處理的同學們一定深知Matrix的強大,很多常見的影象變換一個Matrix就能搞定,甚至更復雜的也是如此。
// Matrix matrix = new Matrix();// 每一種變化都包括set,pre,post三種,分別為設定、矩陣先乘、矩陣後乘。
平移:matrix.setTranslate()
縮放:matrix.setScale()
旋轉:matrix.setRotate()
斜切:matrix.setSkew()
下面我舉兩個例子說明一下。
旋轉
藉助Matrix的postRotate方法旋轉一定角度。
程式碼8.png
縮放
藉助Matrix的postScale方法旋轉一定角度。
程式碼9.png
Bitmap本身也帶了一個縮放方法,不過是把bitmap縮放到目標大小,原理也是用Matrix,我們封裝一下:
程式碼10.png
通過組合可以實現更多效果。
8
裁剪
圖片的裁剪的應用場景還是很多的:頭像剪下,照片裁剪,圓角,圓形等等。
矩形
矩陣形狀的裁剪比較簡單,直接用createBitmap方法即可:
程式碼11.png
圓角
對於圓角我們需要藉助Xfermode和PorterDuffXfermode,把圓角矩陣套在原Bitmap上取交集得到圓角Bitmap。
程式碼12.png
圓形
和上面的圓角裁剪原理相同,不過畫的是圓形套在上面。
為了從中間裁剪出圓形,我們需要計算繪製原始Bitmap的left和top值。
程式碼13.png
從圓角、圓形的處理上我們應該能看的出來繪製任意多邊形都是可以的。
9
儲存Bitmap
很多圖片應用都支援裁剪功能,濾鏡功能等等,最終還是需要把處理後的Bitmap儲存到本地,不然就是再強大的功能也是白忙活了。
程式碼14.png
如果想更穩定或者更簡單的儲存到SDCard的包名路徑下,可以再封裝一下:
程式碼15.png
10
巨圖載入
巨圖載入,當然不能使用常規方法,必OOM。
原理比較簡單,系統中有一個類BitmapRegionDecoder:
程式碼16.png
可以按區域載入:
程式碼17.png
微博的大圖瀏覽也是通過這個BitmapRegionDecoder實現的,具體可自行查閱。
11
顏色矩陣ColorMatrix
影象處理其實是一門很深奧的學科,所幸Android提供了顏色矩陣ColorMatrix類,可實現很多簡單的特效,以灰階效果為例子:
程式碼18.png
除了飽和度,我們還能調整對比度,色相變化等等。
12
ThumbnailUtils剖析
ThumbnailUtils是系統提供的一個專門生成縮圖的方法
13
小結
既然與Bitmap經常打交道,那就把它都理清楚弄明白,這是很有必要的。