Android圖片壓縮(二次采樣)

一捂贿、簡(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);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末烫幕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子敞映,更是在濱河造成了極大的恐慌较曼,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驱显,死亡現(xiàn)場(chǎng)離奇詭異诗芜,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)埃疫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)孩哑,“玉大人栓霜,你說(shuō)我怎么就攤上這事『嵫眩” “怎么了胳蛮?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)丛晌。 經(jīng)常有香客問(wèn)我仅炊,道長(zhǎng),這世上最難降的妖魔是什么澎蛛? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任抚垄,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呆馁。我一直安慰自己桐经,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布浙滤。 她就那樣靜靜地躺著阴挣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纺腊。 梳的紋絲不亂的頭發(fā)上畔咧,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音揖膜,去河邊找鬼盒卸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛次氨,可吹牛的內(nèi)容都是我干的蔽介。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼煮寡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼虹蓄!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起幸撕,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤薇组,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后坐儿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體律胀,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年貌矿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了炭菌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逛漫,死狀恐怖黑低,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酌毡,我是刑警寧澤克握,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站枷踏,受9級(jí)特大地震影響菩暗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜旭蠕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一停团、第九天 我趴在偏房一處隱蔽的房頂上張望旷坦。 院中可真熱鬧,春花似錦客蹋、人聲如沸塞蹭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)番电。三九已至,卻和暖如春辆琅,著一層夾襖步出監(jiān)牢的瞬間漱办,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工婉烟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留娩井,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓似袁,卻偏偏與公主長(zhǎng)得像洞辣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子昙衅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容