在项目开发中会碰到上传图片或者类似微信选择图片发送或者上传的效果,现在的手机拍照效果都不错,图片动不动就是几MB,直接上传或者发送肯定是不行的,就会涉及到图片的压缩,在发送或者上传之前先对图片进行压缩,然后再进行上传;针对图片压缩,android系统提供了质量压缩、尺寸压缩、采样率压缩等方法。
原图大小(4.10MB)
1、质量压缩
/**
* 质量压缩
*
* @param bmp
* @param file
*/
public static void compressImageToFile(Bitmap bmp, File file) {
//质量压缩的比例,0-100的返回 100代表不进行质量压缩,越靠近0,也就是值越小,质量压缩越厉害
int options = 20;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//把压缩后的数据存放到baos中
bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
try {
//进行缓存
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
质量压缩法压缩后的大小463kb
质量压缩其实就是通过算法抠掉(同化)了图片中的一些某个些点附近相近的像素,达到降低质量减小图片大小的目的;需要注意的是,它其实只能实现对file的影响,对加载这个图片出来的bitmap内存是无法节省的,还是和之前一样大的,因为bitmap在内存中的大小是按照像素计算的,也就是width*height,对于质量压缩,并不会改变图片的真实的像素(像素大小不会改变)。
2、尺寸压缩
/**
* 尺寸压缩
*
* @param bmp
* @param file
*/
public static void compressBitmapToFile(Bitmap bmp, File file) {
//尺寸压缩的倍数,值越大,图片尺寸越小
int ratio = 8;
//压缩bitmap到对应的尺寸
Bitmap result = Bitmap.createBitmap(bmp.getWidth() / ratio, bmp.getHeight() / ratio, Bitmap.Config.ARGB_8888);
//将压缩后的bitmap绘制到画板
Canvas canvas = new Canvas(result);
Rect rect = new Rect(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio);
canvas.drawBitmap(bmp, null, rect, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//将压缩后的数据保存放在baos中,这里仍然可以继续使用质量压缩
result.compress(Bitmap.CompressFormat.JPEG, 100, baos);
//进行缓存
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
尺寸压缩后的大小187kb
尺寸压缩的话就是通过减少单位尺寸的像素值和图片的像素大小,同时也会对减少加载这个图片出来的bitmap的内存。
3、采样率压缩
/**
* 设置图片的采样率,降低图片像素
*
* @param filePath
* @param file
*/
public static void compressBitmap(String filePath, File file) {
//数值越高,图片像素越低
int inSmapleSize = 8;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;为true的时候不会真正加载图片,而是得到图片的宽高信息。
//采样率
options.inSampleSize = inSmapleSize;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 把压缩后的数据存放到baos中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
try {
if (file.exists()) {
file.delete();
} else {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
采样率压缩后的大小为143kb
上面这三种压缩方法是系统提供的压缩方法,使用后图片的大小是减少了不少,但是将压缩后的图片一放大就很容失真,这样很难达到项目的需求,特别是尺寸压缩法,直接改变了图片压缩后的像素大小,那有没有其他方法呢,肯定是有的,这就要涉及到android的图片算法,android图片采用的是skia处理引擎,这是一个开源的处理引擎,是一套基于JPEG处理引擎的第二次开发,去掉用了PEG处理引擎中的去掉一个编码算法---哈夫曼算法,采用的是定长编码算法;这是因为哈夫曼算法比较占用cpu,对于最早的android系统很容造成cpu吃紧;但是解码还是保留了哈夫曼算法,所以可以绕过安卓Bitmap API层,来自己编码实现----修复使用哈夫曼算法。
打开https://github.com/libjpeg-turbo/libjpeg-turbo 下载已经提供好的开源库,打开后你会看到有很多的.c文件和.h头文件,把他编译成.so库文件即可;
这里需要注意将README.md文件中NativeUtil类的包名换成自己的包名:
bitherlibjni.cpp和bitherlibjni.h文件中的NativeUtil类的包名换成自己的包名:
接下来配置AS的NDK环境;
app Model下build.gradle文件的配置:
压缩效果358kb
如果觉得上面压缩方法麻烦的话可以采用第三方库进行图片压缩,Luban就是一个第三方图片压缩库,使用也比较方便;
鲁班图片压缩github地址:
github:[https://github.com/Curzibn/Luban]
github:[https://github.com/Curzibn/Luban]
/**
* 使用鲁班压缩图片
* 在子线程中开启图片压缩
*/
private void compressImageView(final String urlPath) {
new Thread(new Runnable() {
@Override
public void run() {
File file = new File(urlPath);
Luban.with(MainActivity.this)
.load(file)//传入要压缩的图片 这里采用的是单张压缩 也可以采用多张压缩
.ignoreBy(100) // 忽略不压缩图片的大小 如果原图片小于100kb不会进行压缩
// .putGear(Luban.Builder.)//设定压缩档次,默认三挡
.setCompressListener(new OnCompressListener() { //设置回调
@Override
public void onStart() {
// TODO 压缩开始前调用,可以在方法内启动 loading UI
}
@Override
public void onSuccess(File file) {
// TODO 压缩成功后调用,返回压缩后的图片文件
String absolutePath = file.getAbsolutePath();
Log.e("absolutePath--->",absolutePath);
}
@Override
public void onError(Throwable e) {
// TODO 当压缩过程出现问题时调用
}
}).launch(); //启动压缩
}
}).start();
}
压缩效果238kb
代码运行是在android6.0以下的手机,所以并没有动态申请读和写sdcard的权限。