Android中圖檔是以Bitmap形式存在的,Bitmap所占記憶體直接影響應用所占記憶體大小,Bitmap所占記憶體大小計算公式:
圖檔長度 * 圖檔寬度 * 一個像素點占用的位元組數
Bitmap壓縮顔色格式:
圖1.png- 品質壓縮
Bitmap bitmap = BitmapFactory.decodeFile(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int quality = 80;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
byte[] bytes = baos.toByteArray();
bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
圖檔的大小沒有變化,因為品質有壓縮不會減少圖檔的像素,它是在保持像素的前提下改變圖圖檔的位深及透明度,來達到壓縮圖檔的目的。
注: 當compress方法中第一個參數為Bitmap.CompressFormat.PNG時,quality沒有作用,因為png圖檔時無損的,不能進行壓縮。
- 采樣率壓縮
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
設定inSampleSize的值為2時,寬和高都變為原來的1/2。
注:當設定options.inJustDecodeBounds = true時,BitmapFactory解碼圖檔時會傳回空的Bitmap對象,但是也可以傳回Bitmap的寬、高以及MimeType。
- 縮放法壓縮(Martix)
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
Bitmap bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
其中,bitmap的寬高分别縮小了一半,圖檔大小壓縮成1/4。
- RGB_565
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
圖檔大小直接縮小了一半,寬高沒有變。
注意:由于ARGB_4444的畫質慘不忍睹,一般假如對圖檔沒有透明度要求的話,可以改成RGB_565,相比ARGB_8888将節省一半的記憶體開銷。
- createScaledBitmap
Bitmap bitmap = BitmapFactory.decodeFile(path);
bitmap = Bitmap.createScaledBitmap(bitmap, 150, 150, true);
這裡将圖檔壓縮成使用者所期望的寬高。
系統壓縮工具類
從Android 2.2開始系統新增了一個縮略圖ThumbnailUtils類,位于framework包下的android.media.ThumbnailUtils位置,可以幫助我們從mediaprovider中擷取系統中的視訊或圖檔檔案的縮略圖,該類提供了三種靜态方法可以直接調用擷取。
1、extractThumbnail (source, width, height):
/**
* 建立一個指定大小的縮略圖
* @param source 源檔案(Bitmap類型)
* @param width 壓縮成的寬度
* @param height 壓縮成的高度
*/
ThumbnailUtils.extractThumbnail(source, width, height);
2、extractThumbnail(source, width, height, options):
/**
* 建立一個指定大小居中的縮略圖
* @param source 源檔案(Bitmap類型)
* @param width 輸出縮略圖的寬度
* @param height 輸出縮略圖的高度
* @param options 如果options定義為OPTIONS_RECYCLE_INPUT,則回收@param source這個資源檔案
* (除非縮略圖等于@param source)
*
*/
ThumbnailUtils.extractThumbnail(source, width, height, options);
3、createVideoThumbnail(filePath, kind):
/**
* 建立一張視訊的縮略圖
* 如果視訊已損壞或者格式不支援可能傳回null
* @param filePath 視訊檔案路徑 如:/sdcard/android.3gp
* @param kind kind可以為MINI_KIND或MICRO_KIND
*
*/
ThumbnailUtils.createVideoThumbnail(filePath, kind);
壓縮工具類
public class CompressUtils {
/**
* 按品質壓縮
* @param bitmap
* @return
*/
public static Bitmap compressImage(Bitmap bitmap){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//品質壓縮方法,這裡100表示不壓縮,把壓縮後的資料存放到baos中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
//循環判斷如果壓縮後圖檔是否大于100kb,大于繼續壓縮
while ( baos.toByteArray().length / 1024>100) {
//清空baos
baos.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;//每次都減少10
}
//把壓縮後的資料baos存放到ByteArrayInputStream中
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
//把ByteArrayInputStream資料生成圖檔
Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null);
return newBitmap;
}
/**
* 按圖檔尺寸壓縮 參數為路徑
* @param imgPath 圖檔路徑
* @param pixelW 目标圖檔寬度
* @param pixelH 目标圖檔高度
* @return
*/
public static Bitmap compressImageFromPath(String imgPath, int pixelW, int pixelH) {
BitmapFactory.Options options = new BitmapFactory.Options();
// 開始讀入圖檔,此時把options.inJustDecodeBounds 設回true,即隻讀邊不讀内容
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
BitmapFactory.decodeFile(imgPath,options);
options.inJustDecodeBounds = false;
options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelH : pixelW ,pixelW * pixelH );
Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options);
return bitmap;
}
/**
* 按圖檔尺寸壓縮 參數是bitmap
* @param bitmap
* @param pixelW
* @param pixelH
* @return
*/
public static Bitmap compressImageFromBitmap(Bitmap bitmap, int pixelW, int pixelH) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
if( os.toByteArray().length / 1024>1024) {//判斷如果圖檔大于1M,進行壓縮避免在生成圖檔(BitmapFactory.decodeStream)時溢出
os.reset();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);//這裡壓縮50%,把壓縮後的資料存放到baos中
}
ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
BitmapFactory.decodeStream(is, null, options);
options.inJustDecodeBounds = false;
options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelH : pixelW ,pixelW * pixelH );
is = new ByteArrayInputStream(os.toByteArray());
Bitmap newBitmap = BitmapFactory.decodeStream(is, null, options);
return newBitmap;
}
/**
* 對圖檔進行縮放指定大小
* @param bitmap
* @param width
* @param height
* @return
*/
public static Bitmap scaleTo(Bitmap bitmap, int width, int height){
int originalWidth = bitmap.getWidth();
int originalHeight = bitmap.getHeight();
float xScale = (float)width / originalWidth;
float yScale = (float)height / originalHeight;
Matrix matrix = new Matrix();
matrix.setScale(xScale,yScale);
return Bitmap.createBitmap(bitmap, 0, 0, originalWidth, originalHeight, matrix, true);
}
/**
* 以最省記憶體的方式讀取本地資源的圖檔
* @param context
* @param resId
* @return
*/
public static Bitmap readBitmap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 擷取資源圖檔
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
/**
* android源碼提供給我們的動态計算出圖檔的inSampleSize方法
* @param options
* @param minSideLength
* @param maxNumOfPixels
* @return
*/
public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);
int roundedSize;
if (initialSize <= 8) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}
return roundedSize;
}
private static int computeInitialSampleSize(BitmapFactory.Options options,int minSideLength, int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
return lowerBound;
}
if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
return 1;
} else if (minSideLength == -1) {
return lowerBound;
} else {
return upperBound;
}
}
}
有效的處理Bitmap OOM方法
public class BitmapActivity extends Activity {
public ImageView imageView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bitmap_activity);
imageView = (ImageView) findViewById(R.id.imageView1);
ImageAsyncTask task = new ImageAsyncTask(imageView,50,50);
task.execute("http://image.baidu.com/search/down?tn=download&word=download&ie=utf8&fr=detail&url=http%3A%2F%2Fimg1.pconline.com.cn%2Fpiclib%2F200904%2F28%2Fbatch%2F1%2F32910%2F12408824046039mk21hbi75.jpg&thumburl=http%3A%2F%2Fimg2.imgtn.bdimg.com%2Fit%2Fu%3D3414739956%2C4196877666%26fm%3D21%26gp%3D0.jpg");
}
/**
* 計算出壓縮比
* @param options
* @param reqWith
* @param reqHeight
* @return
*/
public int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight)
{
//通過參數options來擷取真實圖檔的寬、高
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;//初始值是沒有壓縮的
if(width > reqWidth || height > reqHeight)
{
//計算出原始寬與現有寬,原始高與現有高的比率
int widthRatio = Math.round((float)width/(float)reqWidth);
int heightRatio = Math.round((float)height/(float)reqHeight);
//選出兩個比率中的較小值,這樣的話能夠保證圖檔顯示完全
inSampleSize = widthRatio < heightRatio ? widthRatio:heightRatio;
}
System.out.println("壓縮比: "+inSampleSize);
return inSampleSize;
}
/**
* 将InputStream轉換為Byte數組
* @param in
* @return
*/
public static byte[] inputStreamToByteArray(InputStream in)
{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while((len = in.read(buffer)) != -1)
{
outputStream.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
in.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return outputStream.toByteArray();
}
class ImageAsyncTask extends AsyncTask<String, Void, Bitmap>
{
public ImageView iv;
public int reqWidth;
public int reqHeight;
public ImageAsyncTask(ImageView imageView,int reqWidth,int reqHeight)
{
this.iv = imageView;
this.reqWidth = reqWidth;
this.reqHeight = reqHeight;
}
@Override
protected Bitmap doInBackground(String... params) {
URL url;
HttpURLConnection connection = null;
InputStream in = null;
Bitmap beforeBitmap = null;
Bitmap afterBitmap = null;
try {
url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
in = connection.getInputStream();
BitmapFactory.Options options = new BitmapFactory.Options();
//設定BitmapFactory.Options的inJustDecodeBounds屬性為true表示禁止為bitmap配置設定記憶體
options.inJustDecodeBounds = true;
byte[] data = inputStreamToByteArray(in);
beforeBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);//這次調用的目的是擷取到原始圖檔的寬、高,但是這次操作是沒有寫記憶體操作的
options.inSampleSize = calculateInSampleSize(options,reqWidth, reqHeight);
//設定這次加載圖檔需要加載到記憶體中
options.inJustDecodeBounds = false;
afterBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
float afterSize = (float)(afterBitmap.getRowBytes()*afterBitmap.getHeight());
System.out.println("壓縮之後的圖檔大小: "+(float)afterSize/1024+"KB");
} catch (Exception e) {
System.out.println(e.toString());
}
return afterBitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
if(result != null)
imageView.setImageBitmap(result);
}
}
}
Github圖檔壓