在開發過程中,我們或多或少的都會接觸到Bitmap這個東西,用的不好的話就會出現OOM問題,同時,也會有壓縮的需求,所以今天就來理一理關於Bitmap的一些內容。
關於Bitmap的Config的理解
A:透明度 R:紅色 G:綠 B:藍
/**
* Possible bitmap configurations. A bitmap configuration describes
* how pixels are stored. This affects the quality (color depth) as
* well as the ability to display transparent/translucent colors.
*/
public enum Config {
ALPHA_8 (1),
RGB_565 (3),
@Deprecated
ARGB_4444 (4),
*/
ARGB_8888 (5);
}
複製程式碼
Bitmap.Config ARGB_4444:每個畫素佔四位,即A=4,R=4,G=4,B=4,那麼一個畫素點佔4+4+4+4=16位
Bitmap.Config ARGB_8888:每個畫素佔四位,即A=8,R=8,G=8,B=8,那麼一個畫素點佔8+8+8+8=32位
Bitmap.Config RGB_565:每個畫素佔四位,即R=5,G=6,B=5,沒有透明度,那麼一個畫素點佔5+6+5=16位
Bitmap.Config ALPHA_8:每個畫素佔四位,只有透明度,沒有顏色。
記憶體計算
一張 1024 * 1024 畫素,採用ARGB8888格式,一個畫素32位,每個畫素就是4位元組,佔有記憶體就是4M若採用RGB565,一個畫素16位,每個畫素就是2位元組,佔有記憶體就是2M。
Glide載入圖片預設格式RGB565,Picasso為ARGB8888,預設情況下,Glide佔用記憶體會比Picasso低,色彩不如Picasso鮮豔,自然清晰度就低。
通常我們優化Bitmap時,當需要做效能優化或者防止OOM(Out Of Memory),我們通常會使用Bitmap.Config.RGB_565這個配置,因為Bitmap.Config.ALPHA_8只有透明度,顯示一般圖片沒有意義,Bitmap.Config.ARGB_4444顯示圖片不清楚,Bitmap.Config.ARGB_8888佔用記憶體最多。
圖片載入
如果我們想要載入一張大圖到記憶體中,如果不進行壓縮的話,那麼很顯然就會出現OOM的崩潰,
譬如我們載入一張5440*3000的大圖到手機上面,如果不進行壓縮處理的話,那麼就會出現OOM。
程式碼清單,
java.lang.OutOfMemoryError
android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:703)
android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:679)
android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:446)
android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:480)
com.example.ly.bitmapdemo.MainActivity.onCreate(MainActivity.java:21)
複製程式碼
導致這種情況的發生的根據原因就是記憶體溢位,Android給每個APP的記憶體都是有限的,所以不能容忍這種情況的發生,所以我們就必須進行壓縮一下。
圖片壓縮
我們在上傳一張圖片到伺服器時一般都會先進行壓縮一下,這樣不僅可以節省流量同時也可以節約上傳的時候。最近碰到專案裡碰到一個問題:頭像上傳時總是出現超時的問題,客戶那邊出現的頻率非常高,而我的手機卻基本沒出現過,檢查了一下原因,大概有兩個:
- 客戶的網速不是太好,3G速度不夠,上傳圖片需要很長時間導致超時產生。
- 圖片的體積很大,佔了3、4M,沒經過壓縮直接上傳,導致超時產生。
針對以上兩種情況,主要對於第二種原因進行優化。總結來說就是將圖片進行壓縮再上傳,經過一系列的操作,發現超時現象基本不會出現了,原先2M的圖片,經過壓縮只有50Kb不到,壓縮率達到60%,效果很明顯。
原理分析
如何將一張大圖壓縮到100kb以下並且保持不失真的特性?這就需要用到下面這個類了。
主要運用BitmapFactory.Options
BitmapFactory.Options縮放圖片主要用到inSample取樣率
inSample = 1,取樣後圖片的寬高為原始寬高 inSample > 1,例如2,寬高均為原圖的寬高的1/2
一個採用ARGB8888的1024 * 1024 的圖片 inSample = 1,佔用記憶體就 1024 * 1024 * 4 = 4M inSample = 2,佔用記憶體就 512 * 512 * 4 = 1M
BitmapFactory 給我們提供了一個解析圖片大小的引數類 BitmapFactory.Options ,把這個類的物件的 inJustDecodeBounds 引數設定為 true,這樣解析出來的 Bitmap 雖然是個 null,但是 options 中可以得到圖片的寬和高以及圖片的型別。得到了圖片實際的寬和高之後我們就可以進行壓縮設定了,主要是計算圖片的取樣率。
// 第一次解析將inJustDecodeBounds設定為true,用以獲取圖片大小,並且不需要將Bitmap物件載入到記憶體中
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options); // 第一次解析
複製程式碼
接下來就需要進行選定壓縮的取樣率了。目前市場上的主流手機解析度一般最低是720*1280了所以就按照此解析度進行壓縮
//原始圖片的寬度與720的比值,然後向上取整這裡為8
int wRatio = (int) Math.ceil(options.outWidth / (float) 720);
//原始圖片的高度與1280的比值,然後向上取整這裡為3
int hRatio = (int) Math.ceil(options.outHeight / (float) 1280);
//獲取取樣率
if (wRatio > 1 && hRatio > 1) {
if (wRatio > hRatio) {
options.inSampleSize = wRatio;
} else {
options.inSampleSize = hRatio;
}
}
複製程式碼
經過上面這個取樣率進行壓縮後的寬和高肯定是小於7201270的,我們計算的結果是:680375
我們來實際比較一下壓縮結果:
原先:5440*3000 如果採用ARGB_8888模式的話,那麼如果不壓縮直接載入到記憶體的話,那麼它將佔:
5440/1024 * 3000/1024 *4 = 62.25M,不崩潰才怪呢~
那麼現在:680*375 見證奇蹟的時候,680/1024 * 375/1024 *4=0.9M
兩者一比較的話,那麼效果還是比較明顯的,相差大約64倍,所以還是可以的。當然了經過上面的壓縮方法,我們將壓縮後的圖片上傳到伺服器的話,那麼將會大大的減少流量同時也會減少上傳超時的機率的。
當然了,如果還嫌大的話,我們可以進一步增加壓縮的比例,可以設定成480*800,那麼這樣的話,質量肯定是有所下降的。
參考連結
1、http://www.jianshu.com/p/3950665e93e6
2、http://www.jianshu.com/p/9aaed7310180
關於作者:
1. 簡書 http://www.jianshu.com/users/18281bdb07ce/latest_articles
2. 部落格 http://crazyandcoder.github.io/