一捂贿、簡(jiǎn)介:
在開發(fā)過(guò)程中,我們或多或少的都會(huì)接觸到Bitmap這個(gè)東西胳嘲,用的不好的話就會(huì)出現(xiàn)OOM問(wèn)題厂僧,同時(shí),也會(huì)有壓縮的需求胎围,可能有人會(huì)說(shuō)吁系,向Glide不是已經(jīng)對(duì)圖片壓縮了么德召,但有時(shí)向圖片上傳到服務(wù)器功能,還得需要我們手動(dòng)處理汽纤,去壓縮圖片后上岗,再上傳,否則蕴坪,會(huì)造成上傳很慢肴掷,尤其是用戶網(wǎng)速不好的時(shí)候,還會(huì)浪費(fèi)用戶流量背传,甚至上傳失敗呆瞻。
二、首先了解一下關(guān)于Bitmap的Config的理解
1径玖、)A:透明度 R:紅色 G:綠 B:藍(lán)
public enum Config {
ALPHA_8 (1),
RGB_565 (3),
@Deprecated
ARGB_4444 (4),
ARGB_8888 (5);
}
Bitmap.Config ARGB_4444:每個(gè)像素占四位痴脾,即A=4,R=4梳星,G=4赞赖,B=4,那么一個(gè)像素點(diǎn)占4+4+4+4=16位
Bitmap.Config ARGB_8888:每個(gè)像素占四位冤灾,即A=8前域,R=8,G=8韵吨,B=8匿垄,那么一個(gè)像素點(diǎn)占8+8+8+8=32位
Bitmap.Config RGB_565:每個(gè)像素占四位,即R=5归粉,G=6椿疗,B=5,沒(méi)有透明度盏浇,那么一個(gè)像素點(diǎn)占5+6+5=16位
Bitmap.Config ALPHA_8:每個(gè)像素占四位变丧,只有透明度,沒(méi)有顏色绢掰。
2痒蓬、)內(nèi)存計(jì)算
一張 1024 * 1024 像素,采用ARGB8888格式滴劲,一個(gè)像素32位攻晒,每個(gè)像素就是4字節(jié),占有內(nèi)存就是4M若采用RGB565班挖,一個(gè)像素16位鲁捏,每個(gè)像素就是2字節(jié),占有內(nèi)存就是2M萧芙。
Glide加載圖片默認(rèn)格式RGB565给梅,Picasso為ARGB8888假丧,默認(rèn)情況下,Glide占用內(nèi)存會(huì)比Picasso低动羽,色彩不如Picasso鮮艷包帚,自然清晰度就低。
通常我們優(yōu)化Bitmap時(shí)运吓,當(dāng)需要做性能優(yōu)化或者防止OOM(Out Of Memory)渴邦,我們通常會(huì)使用Bitmap.Config.RGB_565這個(gè)配置,因?yàn)锽itmap.Config.ALPHA_8只有透明度拘哨,顯示一般圖片沒(méi)有意義谋梭,Bitmap.Config.ARGB_4444顯示圖片不清楚,Bitmap.Config.ARGB_8888占用內(nèi)存最多倦青。
圖片加載
如果我們想要加載一張大圖到內(nèi)存中瓮床,如果不進(jìn)行壓縮的話,那么很顯然就會(huì)出現(xiàn)OOM的崩潰姨夹,
譬如我們加載一張5440*3000的大圖到手機(jī)上面纤垂,如果不進(jìn)行壓縮處理的話矾策,那么就會(huì)出現(xiàn)OOM磷账。
錯(cuò)誤日志如下
java.lang.OutOfMemoryErrorandroid.graphics.BitmapFactory.nativeDe
codeStream(Native
Method)android.graphics.BitmapFactory.decodeStreamInternal(Bitmap
Factory.java:703)android.graphics.BitmapFactory.decodeStream(Bitm
apFactory.java:679)android.graphics.BitmapFactory.decodeFile(Bitma
pFactory.java:446)android.graphics.BitmapFactory.decodeFile(Bitmap
Factory.java:480)com.example.ly.bitmapdemo.MainActivity.onCreate(M
ainActivity.java:21)
導(dǎo)致這種情況的發(fā)生的根據(jù)原因就是內(nèi)存溢出,Android給每個(gè)APP的內(nèi)存都是有限的贾虽,所以不能容忍這種情況的發(fā)生逃糟,所以我們就必須進(jìn)行壓縮一下。
壓縮后的效果如下:
圖片壓縮
我們?cè)谏蟼饕粡垐D片到服務(wù)器時(shí)一般都會(huì)先進(jìn)行壓縮一下蓬豁,這樣不僅可以節(jié)省流量同時(shí)也可以節(jié)約上傳的時(shí)間绰咽。最近碰到項(xiàng)目里碰到一個(gè)問(wèn)題:圖片上傳時(shí),有時(shí)會(huì)出現(xiàn)超時(shí)的問(wèn)題地粪,客戶那邊出現(xiàn)的頻率非常高取募,而我的手機(jī)卻基本沒(méi)出現(xiàn)過(guò),檢查了一下原因蟆技,大概有兩個(gè):
客戶的網(wǎng)速不是太好玩敏,3G速度不夠,上傳圖片需要很長(zhǎng)時(shí)間導(dǎo)致超時(shí)產(chǎn)生质礼。圖片很大旺聚,占了3、4M眶蕉,沒(méi)經(jīng)過(guò)壓縮直接上傳砰粹,導(dǎo)致超時(shí)產(chǎn)生。
針對(duì)以上兩種情況造挽,主要對(duì)于第二種原因進(jìn)行優(yōu)化碱璃∨裕總結(jié)來(lái)說(shuō)就是將圖片進(jìn)行壓縮再上傳,經(jīng)過(guò)一系列的操作嵌器,發(fā)現(xiàn)超時(shí)現(xiàn)象基本不會(huì)出現(xiàn)了界酒,原先2M的圖片,經(jīng)過(guò)壓縮只有50Kb不到嘴秸,壓縮率達(dá)到60%毁欣,效果很明顯。
原理分析
如何將一張大圖壓縮到100kb以下并且保持不失真的特性岳掐?這就需要用到下面這個(gè)類了BitmapFactory.Options
BitmapFactory.Options縮放圖片主要用到inSample采樣率凭疮,
inSample = 1,采樣后圖片的寬高為原始寬高
inSample > 1串述,例如2执解,寬高均為原圖的寬高的1/2
一個(gè)采用ARGB8888的1024 *1024 的圖片
inSample = 1,占用內(nèi)存就 1024 *1024 *4 = 4M
inSample = 2纲酗,占用內(nèi)存就 512 *512 * 4 = 1M
BitmapFactory 給我們提供了一個(gè)解析圖片大小的參數(shù)類 BitmapFactory.Options 衰腌,把這個(gè)類的對(duì)象的 inJustDecodeBounds 參數(shù)設(shè)置為 true,這樣解析出來(lái)的 Bitmap 雖然是個(gè) null觅赊,但是 options 中可以得到圖片的寬和高以及圖片的類型右蕊。得到了圖片實(shí)際的寬和高之后我們就可以進(jìn)行壓縮設(shè)置了,主要是計(jì)算圖片的采樣率吮螺。
//第一次采樣
BitmapFactory.Options options = new BitmapFactory.Options();
//該屬性設(shè)置為true只會(huì)加載圖片的邊框進(jìn)來(lái)饶囚,并不會(huì)加載圖片具體的像素點(diǎn),也就是說(shuō)不會(huì)把圖片加載到內(nèi)存中
options.inJustDecodeBounds = true;
//第一次加載圖片,這時(shí)只會(huì)加載圖片的邊框進(jìn)來(lái)鸠补,并不會(huì)加載圖片中的像素點(diǎn)
BitmapFactory.decodeFile(filePath, options);
//獲得原圖的寬和高
int outWidth = options.outWidth;
int outHeight = options.outHeight;
接下來(lái)就需要進(jìn)行選定壓縮的采樣率了萝风。目前市場(chǎng)上的主流手機(jī)分辨率一般最低是720-1280了所以就按照此分辨率進(jìn)行壓縮
//原始圖片的寬度與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;
}
}
經(jīng)過(guò)上面這個(gè)采樣率進(jìn)行壓縮后的寬和高肯定是小于720-1270的规惰,我們計(jì)算的結(jié)果是:680-375
我們來(lái)實(shí)際比較一下壓縮結(jié)果:
原先:54403000*如果采用ARGB_8888模式的話,那么如果不壓縮直接加載到內(nèi)存的話泉蝌,那么它將占:
5440/1024 *3000/1024 *4 = 62.25M歇万,不崩潰才怪呢~
那么現(xiàn)在:680375*見(jiàn)證奇跡的時(shí)候,680/1024 *375/1024 *4=0.9M
兩者一比較的話梨与,那么效果還是比較明顯的堕花,相差大約64倍,所以還是可以的粥鞋。當(dāng)然了經(jīng)過(guò)上面的壓縮方法缘挽,我們將壓縮后的圖片上傳到服務(wù)器的話,那么將會(huì)大大的減少流量同時(shí)也會(huì)減少上傳超時(shí)的幾率的。
當(dāng)然了壕曼,如果還嫌大的話苏研,我們可以進(jìn)一步增加壓縮的比例,可以設(shè)置成480800*腮郊,那么這樣的話摹蘑,質(zhì)量肯定是有所下降的。
這里是圖片二次采樣的代碼
public class BitmapUtils {
/**
* @param filePath 要加載的圖片路徑
* @param destWidth 顯示圖片的控件寬度
* @param destHeight 顯示圖片的控件的高度
* @return
*/
public static Bitmap getBitmap(String filePath, int destWidth, int destHeight) {
//第一次采樣
BitmapFactory.Options options = new BitmapFactory.Options();
//該屬性設(shè)置為true只會(huì)加載圖片的邊框進(jìn)來(lái)轧飞,并不會(huì)加載圖片具體的像素點(diǎn)
options.inJustDecodeBounds = true;
//第一次加載圖片衅鹿,這時(shí)只會(huì)加載圖片的邊框進(jìn)來(lái),并不會(huì)加載圖片中的像素點(diǎn)
BitmapFactory.decodeFile(filePath, options);
//獲得原圖的寬和高
int outWidth = options.outWidth;
int outHeight = options.outHeight;
//定義縮放比例
int sampleSize = 1;
while (outHeight / sampleSize > destHeight || outWidth / sampleSize > destWidth) {
//如果寬高的任意一方的縮放比例沒(méi)有達(dá)到要求过咬,都繼續(xù)增大縮放比例
//sampleSize應(yīng)該為2的n次冪大渤,如果給sampleSize設(shè)置的數(shù)字不是2的n次冪,那么系統(tǒng)會(huì)就近取值
sampleSize *= 2;
}
/********************************************************************************************/
//至此掸绞,第一次采樣已經(jīng)結(jié)束泵三,我們已經(jīng)成功的計(jì)算出了sampleSize的大小
/********************************************************************************************/
//二次采樣開始
//二次采樣時(shí)我需要將圖片加載出來(lái)顯示,不能只加載圖片的框架衔掸,因此inJustDecodeBounds屬性要設(shè)置為false
options.inJustDecodeBounds = false;
//設(shè)置縮放比例
options.inSampleSize = sampleSize;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
//加載圖片并返回
return BitmapFactory.decodeFile(filePath, options);
}
}