BitMap壓縮以及二次采樣

BitMap壓縮以及二次采樣

標簽: Android


首先我們來了解一下什么是oom简十?

1.什么是OOM?為什么會引起OOM撬腾?

out of Memory(內存溢出)我們都知道Android系統(tǒng)會被每個App分配一個獨立的空間勺远,或者說分配一個Dalvik虛擬機(Dalvik是Google公司自己設計用于Android平臺的虛擬機。Dalvik虛擬機是Google等廠商合作開發(fā)的Android移動設備平臺的核心組成部分之一时鸵。它可以支持已轉換為 .dex(即Dalvik Executable)格式的Java應用程序的運行胶逢,.dex格式是專為Dalvik設計的一種壓縮格式,適合內存和處理器速度有限的系統(tǒng)饰潜。Dalvik 經過優(yōu)化初坠,允許在有限的內存中同時運行多個虛擬機的實例甫男,并且[1]每一個Dalvik 應用作為一個獨立的Linux 進程執(zhí)行贮聂。獨立的進程可以防止在虛擬機崩潰的時候所有程序都被關閉乐尊。)這樣每個APP都可以獨立運行而不相互影響享潜!而Android對于每個 Dalvik虛擬機都會有一個最大內存限制朋譬,如果當前占用的內存加上我們申請的內存資源超過了這個限制 ,系統(tǒng)就會拋出OOM錯誤砂代!另外到忽,這里別和RAM混淆了,即時當前RAM中剩余的內存有1G多者填,但是OOM還是會發(fā)生浩村!別把RAM(物理內存)和OOM扯到一起!另外RAM不足的話占哟,就是殺應用了心墅,而不是僅僅是OOM了! 而這個Dalvik中的最大內存標準榨乎,不同的機型是不一樣的

這里可以獲取系統(tǒng)分給你App多少內存

ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
Log.e("HEHE","最大內存:" + activityManager.getMemoryClass());
//或者
Runtime.getRuntime().maxMemory()來獲取

一怎燥、Bitmap:

Bitmap是Android系統(tǒng)中的圖像處理的最重要類之一。用它可以獲取圖像文件信息蜜暑,進行圖像剪切铐姚、旋轉、縮放等操作肛捍,并可以指定格式保存圖像文件谦屑。
常用方法:
public void recycle()  // 回收位圖占用的內存空間,把位圖標記為Dead
public final boolean isRecycled()  //判斷位圖內存是否已釋放
public final int getWidth() //獲取位圖的寬度
public final int getHeight() //獲取位圖的高度
public final boolean isMutable() //圖片是否可修改
public int getScaledWidth(Canvas canvas) //獲取指定密度轉換后的圖像的度
public int getScaledHeight(Canvas canvas) //獲取指定密度轉換后的圖像的度
public boolean compress(CompressFormat format, int quality, OutputStream stream) //按指定的圖片格式以及畫質篇梭,將圖片轉換為輸出流氢橙。
format:壓縮圖像的格式,如Bitmap.CompressFormat.PNG或 ·Bitmap.CompressFormat.JPEG
quality:畫質,0-100.0表示最低畫質壓縮恬偷,100以最高畫質壓縮悍手。對于PNG等無損格式的圖片,會忽略此項設置袍患。
stream: OutputStream中寫入壓縮數據坦康。
return: 是否成功壓縮到指定的流。
public static Bitmap createBitmap(Bitmap src)  //以src為原圖生成不可變得新圖像
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) //以src為原圖诡延,創(chuàng)建新的圖像滞欠,指定新圖像的高寬以及是否可變。
public static Bitmap createBitmap(int width, int height, Config config) //創(chuàng)建指定格式肆良、大小的位圖
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) //以source為原圖筛璧,創(chuàng)建新的圖片,指定起始坐標以及新圖像的高寬惹恃。
BitmapFactory.Option可設置參數

Option 參數類:

public boolean inJustDecodeBounds //如果設置為true夭谤,不獲取圖片,不分配 內存巫糙,但會返回圖片的高度寬度信息朗儒。
如果將這個值置為true,那么在解碼的時候將不會返回bitmap,只會返回這個bitmap 的尺寸醉锄。這個屬性的目的是乏悄,如果你只想知道一個bitmap的尺寸,但又不想將其加載到內存時恳不。這是一個非常有用的屬性檩小。
public int inSampleSize //圖片縮放的倍數
這個值是一個int,當它小于1的時候妆够,將會被當做1處理识啦,如果大于1负蚊,那么就會按照比例(1 / inSampleSize)縮小bitmap的寬和高神妹、降低分辨率,大于1時這個值將會被處置為2的倍數家妆。例如鸵荠,width=100,height=100伤极,inSampleSize=2蛹找,那么就會將bitmap處理為,width=50哨坪,height=50庸疾,寬高降為1 / 2,像素數降為1 / 4当编。
public int outWidth //獲取圖片的寬度值
public int outHeight //獲取圖片的高度值
表示這個Bitmap的寬和高届慈,一般和inJustDecodeBounds一起使用來獲得Bitmap的寬高,但是不加載到內存忿偷。
public int inDensity //用于位圖的像素壓縮比
public int inTargetDensity //用于目標位圖的像素壓縮比(要生成的位圖)
public byte[] inTempStorage  //創(chuàng)建臨時文件金顿,將圖片存儲
public boolean inScaled //設置為true時進行圖片壓縮,從inDensity到inTargetDensity
public boolean inDither  //如果為true,解碼器嘗試抖動解碼
public Bitmap.Config inPreferredConfig  //設置解碼器
這個值是設置色彩模式鲤桥,默認值是ARGB_8888揍拆,在這個模式下,一個像素點占用4bytes空間茶凳,一般對透明度不做要求的話嫂拴,一般采用RGB_565模式,這個模式下一個像素點占用2bytes贮喧。
public String outMimeType  //設置解碼圖像
public boolean inPurgeable //當存儲Pixel的內存空間在系統(tǒng)內存不足時是否可以被回收
public boolean inInputShareable  //inPurgeable為true情況下才生效顷牌,是否可以共享一個InputStream
public boolean inPreferQualityOverSpeed  //為true則優(yōu)先保證Bitmap質量其次是解碼速度
public boolean inMutable  //配置Bitmap是否可以更改,比如:在Bitmap上隔幾個像素加一條線段
public int inScreenDensity  //當前屏幕的像素密度
從資源中獲取位圖的方式有兩種:通過BitmapDrawable或者BitmapFactory
你可以創(chuàng)建一個構造一個BitmapDrawable對象塞淹,比如通過流構建BitmapDrawable:
BitmapDrawable bmpMeizi = new BitmapDrawable(getAssets().open("pic_meizi.jpg"));
Bitmap mBitmap = bmpMeizi.getBitmap();
img_bg.setImageBitmap(mBitmap);

工廠方法:

public static Bitmap decodeFile(String pathName, Options opts)  //從文件讀取圖片
public static Bitmap decodeFile(String pathName)
public static Bitmap decodeStream(InputStream is)  //從輸入流讀取圖片
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
public static Bitmap decodeResource(Resources res, int id)  //從資源文件讀取圖片
public static Bitmap decodeResource(Resources res, int id, Options opts)
public static Bitmap decodeByteArray(byte[] data, int offset, int length)  //從數組讀取圖片
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
public static Bitmap decodeFileDescriptor(FileDescriptor fd) //從文件讀取文件 與decodeFile不同的是這個直接調用JNI函數進行讀取 效率比較高
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)
首先了解一下關于Bitmap的Config的理解
A:透明度 R:紅色 G:綠 B:藍
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:每個像素占四位择膝,只有透明度誓琼,沒有顏色。

bitmap內存大小 = 圖片長度 x 圖片寬度 x 單位像素占用的字節(jié)數
起決定因素就是最后那個參數了肴捉,Bitmap'常見有2種編碼方式:ARGB_8888和RGB_565腹侣,ARGB_8888每個像素點4個byte,RGB_565是2個byte齿穗,一般都采用ARGB_8888這種傲隶。那么常見的1080*1920的圖片內存占用就是:
1920 x 1080 x 4 = 7.9M

常用的壓縮方法:

1.質量壓縮

private void compressQuality() {
    Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test);
    mSrcSize = bm.getByteCount() + "byte";
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);
    byte[] bytes = bos.toByteArray();
    mSrcBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
}

質量壓縮不會減少圖片的像素,它是在保持像素的前提下改變圖片的位深及透明度窃页,來達到壓縮圖片的目的跺株,圖片的長,寬腮出,像素都不會改變帖鸦,那么bitmap所占內存大小是不會變的。
我們可以看到有個參數:quality胚嘲,可以調節(jié)你壓縮的比例作儿,但是還要注意一點就是,質量壓縮堆png格式這種圖片沒有作用馋劈,因為png是無損壓縮攻锰。

2.采樣率壓縮

private void compressSampling() {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 2;
    mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test, options);
}

采樣率壓縮其原理其實也是縮放bitamp的尺寸,通過調節(jié)其inSampleSize參數妓雾,比如調節(jié)為2娶吞,寬高會為原來的1/2,內存變回原來的1/4.

3.放縮法壓縮

private void compressMatrix() {
    Matrix matrix = new Matrix();
    matrix.setScale(0.5f, 0.5f);
    Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test);
    mSrcBitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
    bm = null;
}

放縮法壓縮使用的是通過矩陣對圖片進行裁剪械姻,也是通過縮放圖片尺寸妒蛇,來達到壓縮圖片的效果,和采樣率的原理一樣

4.RGB_565壓縮

private void compressRGB565() {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.RGB_565;
    mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test, options);
}

這是通過壓縮像素占用的內存來達到壓縮的效果,一般不建議使用ARGB_4444绣夺,因為畫質實在是辣雞吏奸,如果對透明度沒有要求,建議可以改成RGB_565陶耍,相比ARGB_8888將節(jié)省一半的內存開銷奋蔚。

5.createScaledBitmap

private void compressScaleBitmap() {
    Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test);
    mSrcBitmap = Bitmap.createScaledBitmap(bm, 600, 900, true);
    bm = null;
}

將圖片的大小壓縮成用戶的期望大小,來減少占用內存烈钞。

為什么要二次采樣

默認情況下泊碑,bitmap每個像素點占用4個字節(jié)(ARGB_8888),比如一張3543×3503的圖片差不多在內存中占用47M
安卓系統(tǒng)給每個應用分配的內存都是有限的毯欣,可以使用Runtime.getRuntime().maxMemory()來獲取
內存有限空間馒过,默認情況下圖片存儲又需要大量的空間,于是就容易產生OOM(內存溢出)

 private Bitmap getCompressBm(int id, int maxw, int maxh) {
        Bitmap bm = null;
        int iSamplesize = 1;
        //第一次采樣
        BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();
        //該屬性設置為true只會加載圖片的邊框進來仪媒,并不會加載圖片具體的像素點
        bitmapFactoryOptions.inJustDecodeBounds = true;
        bm = BitmapFactory.decodeResource(getResources(), id, bitmapFactoryOptions);
        Log.e("Tag", "onActivityResult: 壓縮之前圖片的寬:" + bitmapFactoryOptions.outWidth + "--壓縮之前圖片的高:"
                + bitmapFactoryOptions.outHeight + "--壓縮之前圖片大小:" + bitmapFactoryOptions.outWidth * bitmapFactoryOptions.outHeight * 4 / 1024  + "kb");
        int iWidth = bitmapFactoryOptions.outWidth;
        int iHeight = bitmapFactoryOptions.outHeight;
        //對縮放比例進行調整沉桌,直到寬和高符合我們要求為止
        while (iWidth > maxw|| iHeight > maxh){
           //如果寬高的任意一方的縮放比例沒有達到要求谢鹊,都繼續(xù)增大縮放比例
            //sampleSize應該為2的n次冪算吩,如果給sampleSize設置的數字不是2的n次冪,那么系統(tǒng)會就近取
            iSamplesize = iSamplesize*2;//寬高均為原圖的寬高的1/2  內存約為原來的1/4
            iWidth = iWidth/iSamplesize;
            iHeight = iHeight/iSamplesize;
        }
        //二次采樣開始
        //二次采樣時我需要將圖片加載出來顯示佃扼,不能只加載圖片的框架偎巢,因此inJustDecodeBounds屬性要設置為false
        bitmapFactoryOptions.inJustDecodeBounds = false;
        bitmapFactoryOptions.inSampleSize = iSamplesize;
        // 設置像素顏色信息
       // bitmapFactoryOptions.inPreferredConfig = Bitmap.Config.RGB_565;
        bm = BitmapFactory.decodeResource(getResources(),id, bitmapFactoryOptions);
//默認的圖片格式是Bitmap.Config.ARGB_8888
        Log.e("Tag", "onActivityResult: 圖片的寬:" + bm.getWidth() + "--圖片的高:"
                + bm.getHeight() + "--圖片大小:" + bm.getWidth() * bm.getHeight() * 4 / 1024  + "kb");
        return bm;//返回壓縮后的照片
    }

再一次感謝您花費時間閱讀這份文章,祝您在這里記錄兼耀、閱讀压昼、分享愉快!

作者 @windrain_boy
2017年06月06日

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末瘤运,一起剝皮案震驚了整個濱河市窍霞,隨后出現的幾起案子,更是在濱河造成了極大的恐慌拯坟,老刑警劉巖但金,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異郁季,居然都是意外死亡冷溃,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門梦裂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來似枕,“玉大人,你說我怎么就攤上這事年柠≡浼撸” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長答憔。 經常有香客問我牵咙,道長,這世上最難降的妖魔是什么攀唯? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任洁桌,我火速辦了婚禮,結果婚禮上侯嘀,老公的妹妹穿的比我還像新娘另凌。我一直安慰自己,他們只是感情好戒幔,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布吠谢。 她就那樣靜靜地躺著,像睡著了一般诗茎。 火紅的嫁衣襯著肌膚如雪工坊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天敢订,我揣著相機與錄音王污,去河邊找鬼。 笑死楚午,一個胖子當著我的面吹牛昭齐,可吹牛的內容都是我干的。 我是一名探鬼主播矾柜,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼阱驾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怪蔑?” 一聲冷哼從身側響起里覆,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缆瓣,沒想到半個月后喧枷,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡捆愁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年割去,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昼丑。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡呻逆,死狀恐怖,靈堂內的尸體忽然破棺而出菩帝,到底是詐尸還是另有隱情咖城,我是刑警寧澤茬腿,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站宜雀,受9級特大地震影響切平,放射性物質發(fā)生泄漏。R本人自食惡果不足惜辐董,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一悴品、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧简烘,春花似錦苔严、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至覆旭,卻和暖如春退子,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背型将。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工寂祥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人茶敏。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓壤靶,卻偏偏與公主長得像缚俏,于是被迫代替她去往敵國和親惊搏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內容