Android中图片是以Bitmap形式存在的,Bitmap所占内存直接影响应用所占内存大小,Bitmap所占内存大小计算公式:
图片长度 * 图片宽度 * 一个像素点占用的字节数
Bitmap压缩颜色格式:
- 质量压缩
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图片压