最近公司配套智能自行车的App要做发布骑行活动的功能,这就不可避免的要模仿微信朋友圈的很多功能了,这一次我们主要介绍如何批量上传图片,并且不过分降低图片的观赏质量。
关键代码如下(这个是简化版的),已经封装成了方法,可以直接复制调用,参数是图片在手机中的地址,返回结果是bitmap所对应的字节码数组:
/**
* 将图片进行高质量压缩
*/
public static byte[] makeSmallBitmapHeightQuality(String dir) {
// 首先获得bitmap的宽高
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true; // 只获取图片的框架
BitmapFactory.decodeFile(dir, bitmapOptions); // 获得只有框架的bitmap
// 首先获得图宽高并进行压缩,以
float width = bitmapOptions.outWidth;
float height = bitmapOptions.outHeight;
float standardWidth = 320f;
float standardHeight = 480f;
if (width > height && width > standardWidth) { // 将图片的宽高按照 320 * 480 的手机来压缩
float scale = width / standardWidth;
bitmapOptions.inSampleSize = (int) (scale + .5);
} else if (height > width && height > standardHeight) {
float scale = 1 - standardHeight / height;
bitmapOptions.inSampleSize = (int) (scale + .5);
}
bitmapOptions.inJustDecodeBounds = false;
Bitmap bitmapScaled = BitmapFactory.decodeFile(dir, bitmapOptions); // 获取缩小后的图片
// 将bitmap通过压缩变味流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmapScaled.compress(Bitmap.CompressFormat.JPEG, 50, baos); // 这里压缩图片质量,0 ---> 100,把压缩后的数据存放到baos中
System.gc(); // 压缩图片需要耗费大量资源,申请回收多余资源
// 将压缩获取到的流生成字节码数组,准备用于网络传输
byte[] imgBytes = baos.toByteArray();
// 关流
try {
baos.close();
baos = null;
} catch (IOException e) {
e.printStackTrace();
baos = null;
}
return imgBytes;
}
上面的方法总共就做了两件事情:
一、首先将原bitmap的宽高等比例缩小到符合 320*480 屏幕分辨率手机的大小
二、压缩这张已经被缩小的bitmap的质量,上面简化版的方法直接写死了将图片质量压缩一半。
使用注意事项:
图片质量压缩是一个非常耗费时间和内存的过程,因此在进行图片轮流压缩之前,需要进行三步操作:
1)首先,我们需要开启一个子线程,这样耗时的问题可以解决了
2)其次,我们需要开启一个服务,并且在服务里面公开一个方法,专门用于调用图片压缩的方法
3)打开项目的清单文件,在新注册的服务中添加 process 属性,这个属性的值可以随便写。定义了这个属性就等于是为这个处理图片压缩的服务单独开启了一个新的进程,process属性的值就是进程的名字,同时这个服务也就拥有了一个全新的运行内存(应该是22M ~ 25M),这样,批量压缩图片所导致的OOM错误也就规避了。
讲完了简单版的图片压缩方式,下面我们再来看一下稍微复杂一点的图片压缩方法(其实只是在原有的基础上添加了一个循环),关键代码如下:
/**
* 将图片进行高质量压缩
*/
public static byte[] makeSmallBitmapHeightQuality(String dir) {
// 首先获得bitmap的宽高
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true; // 只获取图片的框架
BitmapFactory.decodeFile(dir, bitmapOptions); // 获得只有框架的bitmap
// 首先获得图宽高并进行压缩,以
float width = bitmapOptions.outWidth;
float height = bitmapOptions.outHeight;
float standardWidth = 320f;
float standardHeight = 480f;
if (width > height && width > standardWidth) { // 将图片的宽高按照 320 * 480 的手机来压缩
float scale = width / standardWidth;
bitmapOptions.inSampleSize = (int) (scale + .5);
} else if (height > width && height > standardHeight) {
float scale = 1 - standardHeight / height;
bitmapOptions.inSampleSize = (int) (scale + .5);
}
bitmapOptions.inJustDecodeBounds = false;
Bitmap bitmapScaled = BitmapFactory.decodeFile(dir, bitmapOptions); // 获取缩小后的图片
// 将bitmap通过压缩变为输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 重新设置压缩质量进行备用
int options = 40;
while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100KB,大于继续压缩
baos.reset(); // 重置baos即清空baos
options -= 10;// 每次质量都减少10
bitmapScaled.compress(Bitmap.CompressFormat.JPEG, options, baos);// 在这里压缩图片质量,0 ---> 100,把压缩后的数据存放到baos中
}
System.gc(); // 压缩图片需要耗费大量资源,回收它
// 将压缩获取到的流生成字节码数组,准备用于网络传输
byte[] imgBytes = baos.toByteArray();
// 关流
try {
baos.close();
baos = null;
} catch (IOException e) {
e.printStackTrace();
baos = null;
}
return imgBytes;
}
上面的代码只是在简单版的方法之上添加了一个 while 循环判断,根据图片的大小来设置图片的压缩质量,保证最终上传的图片都小于 100KB,这是简单版的方法无法保证的。
使用注意事项:
和简化版的图片压缩方法的使用注意事项一致。
整体总结:由于批量质量压缩Bitmap图片并上传需要较多时间,但是微信朋友圈上传批量图片却非常迅速,个人的猜测是:
微信客户端实际上是先将本地编辑好的数据图片直接展示在了朋友圈中(图片展示之前还是经过了宽高等比例压缩的,但是没有进行耗时的质量压缩),然后此时再去子线程开启的服务中去进行实际的压缩并上传这些图片,这极大优化了用户的体验。如果这段时间网络较差,微信也是会告知图片上传失败的,这个应该就是它启用后台服务进行批量压缩图片并上传的最好证明了。