最近碰到一个图片上传的问题,普通相机拍出的图片基本在1M以上,并不适合用作于图片上传,于是就用到了这个BitmapFactory.compress()
方法,来实现对图片进行压缩,来看一下具体的压缩操作。
1.inSampleSize参数
在BitmapFactory
中有一个内部类BitmapFactory.Options
,其中当options.inSampleSize
值>1时,根据文档:
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. (1 -> decodes full size; 2 -> decodes 1/4th size; 4 -> decode 1/16th size). Because you rarely need to show and have full size bitmap images on your phone. For manipulations smaller sizes are usually enough.
也就是说,options.inSampleSize
是以2的指数的倒数被进行放缩。这样,我们可以依靠inSampleSize
的值的设定将图片放缩载入,这样一般情况也就不会出现上述的OOM
问题了。现在问题是怎么确定inSampleSize
的值?每张图片的放缩大小的比例应该是不一样的!这样的话就要运行时动态确定。在BitmapFactory.Options
中提供了另一个成员inJustDecodeBounds
。
2.inJustDecodeBounds参数
BitmapFactory.Options option = new BitmapFactory.Options();
option.inJustDecodeBounds = true;
BitmapFactory.decodeFile(srcPath, option);
设置inJustDecodeBounds
为true后,decodeFile
并不分配空间,但可计算出原始图片的长度和宽度,即opts.width
和opts.height
。有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize
。
3.具体执行操作
public static String compressImage(String srcPath) {
//1.先获取到图片的宽高
BitmapFactory.Options option = new BitmapFactory.Options();
option.inJustDecodeBounds = true;
BitmapFactory.decodeFile(srcPath, option);
int actualWidth = option.outWidth;
int actualHeight = option.outHeight;
//设置想要压缩的图片大小
int desiredHeight = 800;
int desiredWidth = 480;
//2.设置inJustDecodeBounds 为false
option.inJustDecodeBounds = false;
option.inSampleSize = findBestSampleSize(actualWidth, actualHeight,
desiredWidth, desiredHeight);//设置inSampleSize大小
//3.重新设置Bitmap
Bitmap tempBitmap = BitmapFactory.decodeFile(srcPath, option);
//4.压缩流到一定大小(注意,上面的宽高压缩和下面的大小压缩是两个概念,先看下面)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
tempBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);//100是保留原100%的品质,压缩0% 作用:输出到baos 流里面去
int options = 90;
//判断,如果图片大于200kb,继续压缩
while (baos.toByteArray().length / 1024 > 200) {
baos.reset();
tempBitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里才是真正进行压缩
options -= 10;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
//5.保存到本地,并返回图片的路径
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
File file = null;
try {
File dir = Constants.me().getExternalDir();
if (!dir.exists()) {
dir.mkdirs();
}
String filename = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date()) + ".jpeg";
file = new File(dir, filename);
fos = new FileOutputStream(file);
while ((len = isBm.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fos.flush();
} catch (IOException e) {
Log4a.e(e);
} finally {
if (isBm != null) {
try {
isBm.close();
} catch (IOException e) {
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
}
}
if (tempBitmap != null) {
tempBitmap.recycle();
}
}
return file == null ? srcPath : file.getAbsolutePath();
}
//这个方法用来计算inSampleSize大小
static int findBestSampleSize(int actualWidth, int actualHeight,
int desiredWidth, int desiredHeight) {
double wr = (double) actualWidth / desiredWidth;
double hr = (double) actualHeight / desiredHeight;
double ratio = Math.min(wr, hr);
float n = 1.0f;
//如果最小的还>2 则让比例*2
while ((n * 2) <= ratio) {
n *= 2;
}
return (int) n;
}
4.Bitmap占用的存储大小和内存大小区别解释
该链接是例子讲解区别的
一句话总结就是:Bitmap.compress方法确实可以压缩图片,但压缩的是存储大小 如果想要显示的Bitmap占用的内存少一点,还是需要去设置加载的像素长度和宽度(变成缩略图)