圖片壓縮就是為了避免我們內(nèi)存溢出,所有要對一系列進(jìn)行壓縮二次采樣等
1.什么是OOM堡赔?為什么會引起OOM识脆?
out of Memory(內(nèi)存溢出)我們都知道Android系統(tǒng)會被每個App分配一個獨(dú)立的空間,或者說分配一個Dalvik虛擬機(jī)(Dalvik是Google公司自己設(shè)計用于Android平臺的虛擬機(jī)善已。Dalvik虛擬機(jī)是Google等廠商合作開發(fā)的Android移動設(shè)備平臺的核心組成部分之一灼捂。它可以支持已轉(zhuǎn)換為 .dex(即Dalvik Executable)格式的Java應(yīng)用程序的運(yùn)行,.dex格式是專為Dalvik設(shè)計的一種壓縮格式换团,適合內(nèi)存和處理器速度有限的系統(tǒng)悉稠。Dalvik經(jīng)過優(yōu)化,允許在有限的內(nèi)存中同時運(yùn)行多個虛擬機(jī)的實例艘包,并且[1]每一個Dalvik應(yīng)用作為一個獨(dú)立的Linux 進(jìn)程執(zhí)行的猛。獨(dú)立的進(jìn)程可以防止在虛擬機(jī)崩潰的時候所有程序都被關(guān)閉。)這樣每個APP都可以獨(dú)立運(yùn)行而不相互影響想虎!而Android對于每個 Dalvik虛擬機(jī)都會有一個最大內(nèi)存限制卦尊,如果當(dāng)前占用的內(nèi)存加上我們申請的內(nèi)存資源超過了這個限制 ,系統(tǒng)就會拋出OOM錯誤舌厨!另外岂却,這里別和RAM混淆了,即時當(dāng)前RAM中剩余的內(nèi)存有1G多邓线,但是OOM還是會發(fā)生淌友!別把RAM(物理內(nèi)存)和OOM扯到一起!另外RAM不足的話骇陈,就是殺應(yīng)用了震庭,而不是僅僅是OOM了! 而這個Dalvik中的最大內(nèi)存標(biāo)準(zhǔn)你雌,不同的機(jī)型是不一樣的
這里可以獲取系統(tǒng)分給你App多少內(nèi)存
ActivityManager?activityManager =?(ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);?Log.e("HEHE","最大內(nèi)存:"?+?activityManager.getMemoryClass());
一器联、Bitmap:
Bitmap是Android系統(tǒng)中的圖像處理的最重要類之一二汛。用它可以獲取圖像文件信息,進(jìn)行圖像剪切拨拓、旋轉(zhuǎn)肴颊、縮放等操作,并可以指定格式保存圖像文件渣磷。
常用方法:
public void recycle() // 回收位圖占用的內(nèi)存空間婿着,把位圖標(biāo)記為Dead
public final boolean isRecycled() //判斷位圖內(nèi)存是否已釋放
public final int getWidth() //獲取位圖的寬度
public final int getHeight() //獲取位圖的高度
public final boolean isMutable() //圖片是否可修改
public int getScaledWidth(Canvas canvas) //獲取指定密度轉(zhuǎn)換后的圖像的度
public int getScaledHeight(Canvas canvas) //獲取指定密度轉(zhuǎn)換后的圖像的度
public boolean compress(CompressFormat format, int quality, OutputStream stream) //按指定的圖片格式以及畫質(zhì),將圖片轉(zhuǎn)換為輸出流醋界。
format:壓縮圖像的格式,如Bitmap.CompressFormat.PNG或 ·Bitmap.CompressFormat.JPEG
quality:畫質(zhì)竟宋,0-100.0表示最低畫質(zhì)壓縮,100以最高畫質(zhì)壓縮形纺。對于PNG等無損格式的圖片丘侠,會忽略此項設(shè)置。
stream: OutputStream中寫入壓縮數(shù)據(jù)逐样。
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)建新的圖片担神,指定起始坐標(biāo)以及新圖像的高寬。
BitmapFactory.Option可設(shè)置參數(shù)
Option 參數(shù)類:
public boolean inJustDecodeBounds //如果設(shè)置為true始花,不獲取圖片妄讯,不分配 內(nèi)存,但會返回圖片的高度寬度信息酷宵。
如果將這個值置為true亥贸,那么在解碼的時候?qū)⒉粫祷豣itmap,只會返回這個bitmap 的尺寸浇垦。這個屬性的目的是炕置,如果你只想知道一個bitmap的尺寸,但又不想將其加載到內(nèi)存時男韧。這是一個非常有用的屬性朴摊。
public int inSampleSize //圖片縮放的倍數(shù)
這個值是一個int,當(dāng)它小于1的時候此虑,將會被當(dāng)做1處理甚纲,如果大于1,那么就會按照比例(1 / inSampleSize)縮小bitmap的寬和高朦前、降低分辨率介杆,大于1時這個值將會被處置為2的倍數(shù)鹃操。例如,width=100春哨,height=100荆隘,inSampleSize=2,那么就會將bitmap處理為赴背,width=50椰拒,height=50,寬高降為1 / 2凰荚,像素數(shù)降為1 / 4耸三。
public int outWidth //獲取圖片的寬度值
public int outHeight //獲取圖片的高度值
表示這個Bitmap的寬和高,一般和inJustDecodeBounds一起使用來獲得Bitmap的寬高浇揩,但是不加載到內(nèi)存。
public int inDensity //用于位圖的像素壓縮比
public int inTargetDensity //用于目標(biāo)位圖的像素壓縮比(要生成的位圖)
public byte[] inTempStorage //創(chuàng)建臨時文件憨颠,將圖片存儲
public boolean inScaled //設(shè)置為true時進(jìn)行圖片壓縮胳徽,從inDensity到inTargetDensity
public boolean inDither //如果為true,解碼器嘗試抖動解碼
public Bitmap.Config inPreferredConfig //設(shè)置解碼器
這個值是設(shè)置色彩模式,默認(rèn)值是ARGB_8888爽彤,在這個模式下养盗,一個像素點(diǎn)占用4bytes空間,一般對透明度不做要求的話适篙,一般采用RGB_565模式往核,這個模式下一個像素點(diǎn)占用2bytes。
public String outMimeType //設(shè)置解碼圖像
public boolean inPurgeable //當(dāng)存儲Pixel的內(nèi)存空間在系統(tǒng)內(nèi)存不足時是否可以被回收
public boolean inInputShareable //inPurgeable為true情況下才生效嚷节,是否可以共享一個InputStream
public boolean inPreferQualityOverSpeed //為true則優(yōu)先保證Bitmap質(zhì)量其次是解碼速度
public boolean inMutable //配置Bitmap是否可以更改聂儒,比如:在Bitmap上隔幾個像素加一條線段
public int inScreenDensity //當(dāng)前屏幕的像素密度
從資源中獲取位圖的方式有兩種:通過BitmapDrawable或者BitmapFactory
你可以創(chuàng)建一個構(gòu)造一個BitmapDrawable對象,比如通過流構(gòu)建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) //從數(shù)組讀取圖片
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
public static Bitmap decodeFileDescriptor(FileDescriptor fd) //從文件讀取文件 與decodeFile不同的是這個直接調(diào)用JNI函數(shù)進(jìn)行讀取 效率比較高
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)
首先了解一下關(guān)于Bitmap的Config的理解
A:透明度 R:紅色 G:綠 B:藍(lán)
Bitmap.Config ARGB_4444:每個像素占四位硫痰,即A=4衩婚,R=4,G=4效斑,B=4非春,那么一個像素點(diǎn)占4+4+4+4=16位
Bitmap.Config ARGB_8888:每個像素占四位,即A=8缓屠,R=8奇昙,G=8,B=8敌完,那么一個像素點(diǎn)占8+8+8+8=32位
Bitmap.Config RGB_565:每個像素占四位储耐,即R=5,G=6蠢挡,B=5弧岳,沒有透明度凳忙,那么一個像素點(diǎn)占5+6+5=16位
Bitmap.Config ALPHA_8:每個像素占四位,只有透明度禽炬,沒有顏色涧卵。
bitmap內(nèi)存大小 = 圖片長度 x 圖片寬度 x 單位像素占用的字節(jié)數(shù)
起決定因素就是最后那個參數(shù)了,Bitmap'常見有2種編碼方式:ARGB_8888和RGB_565腹尖,ARGB_8888每個像素點(diǎn)4個byte柳恐,RGB_565是2個byte,一般都采用ARGB_8888這種热幔。那么常見的1080*1920的圖片內(nèi)存占用就是:
1920 x 1080 x 4 = 7.9M
常用的壓縮方法:
1.質(zhì)量壓縮
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); }
質(zhì)量壓縮不會減少圖片的像素乐设,它是在保持像素的前提下改變圖片的位深及透明度,來達(dá)到壓縮圖片的目的绎巨,圖片的長近尚,寬,像素都不會改變场勤,那么bitmap所占內(nèi)存大小是不會變的戈锻。
我們可以看到有個參數(shù):quality,可以調(diào)節(jié)你壓縮的比例和媳,但是還要注意一點(diǎn)就是格遭,質(zhì)量壓縮堆png格式這種圖片沒有作用,因為png是無損壓縮留瞳。
2.采樣率壓縮
private void compressSampling() { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test, options); }
采樣率壓縮其原理其實也是縮放bitamp的尺寸拒迅,通過調(diào)節(jié)其inSampleSize參數(shù),比如調(diào)節(jié)為2她倘,寬高會為原來的1/2璧微,內(nèi)存變回原來的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; }
放縮法壓縮使用的是通過矩陣對圖片進(jìn)行裁剪,也是通過縮放圖片尺寸硬梁,來達(dá)到壓縮圖片的效果往毡,和采樣率的原理一樣
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); }
這是通過壓縮像素占用的內(nèi)存來達(dá)到壓縮的效果潜沦,一般不建議使用ARGB_4444输硝,因為畫質(zhì)實在是辣雞,如果對透明度沒有要求壳澳,建議可以改成RGB_565罩息,相比ARGB_8888將節(jié)省一半的內(nèi)存開銷嗤详。
5.createScaledBitmap
private void compressScaleBitmap() { Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test); mSrcBitmap = Bitmap.createScaledBitmap(bm, 600, 900, true); bm = null; }
將圖片的大小壓縮成用戶的期望大小,來減少占用內(nèi)存瓷炮。
圖片二次采樣
private Bitmap getCompressBm(int id, int maxw, int maxh) {
Bitmap bm = null;
int iSamplesize = 1;
//第一次采樣
BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();
//該屬性設(shè)置為true只會加載圖片的邊框進(jìn)來葱色,并不會加載圖片具體的像素點(diǎn)
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;
//對縮放比例進(jìn)行調(diào)整,直到寬和高符合我們要求為止
while (iWidth > maxw|| iHeight > maxh){
//如果寬高的任意一方的縮放比例沒有達(dá)到要求娘香,都繼續(xù)增大縮放比例
//sampleSize應(yīng)該為2的n次冪苍狰,如果給sampleSize設(shè)置的數(shù)字不是2的n次冪办龄,那么系統(tǒng)會就近取
iSamplesize = iSamplesize*2;//寬高均為原圖的寬高的1/2 內(nèi)存約為原來的1/4
iWidth = iWidth/iSamplesize;
iHeight = iHeight/iSamplesize;
}
//二次采樣開始
//二次采樣時我需要將圖片加載出來顯示,不能只加載圖片的框架淋昭,因此inJustDecodeBounds屬性要設(shè)置為false
bitmapFactoryOptions.inJustDecodeBounds = false;
bitmapFactoryOptions.inSampleSize = iSamplesize;
// 設(shè)置像素顏色信息
// bitmapFactoryOptions.inPreferredConfig = Bitmap.Config.RGB_565;
bm = BitmapFactory.decodeResource(getResources(),id, bitmapFactoryOptions);
//默認(rèn)的圖片格式是Bitmap.Config.ARGB_8888
Log.e("Tag", "onActivityResult: 圖片的寬:" + bm.getWidth() + "--圖片的高:"
+ bm.getHeight() + "--圖片大小:" + bm.getWidth() * bm.getHeight() * 4 / 1024 + "kb");
return bm;//返回壓縮后的照片
}