github连接:
github.com/xiaojigugu/…java
线程池+队列+观察者模式+建造者模式 实现多线程图片压缩android
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
复制代码
dependencies {
implementation 'com.github.xiaojigugu:MasterImageCompress:1.0.1'
}
复制代码
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
复制代码
//配置压缩条件
CompressConfig compressConfig = CompressConfig
.builder()
.keepSource(true) //是否保留源文件
//压缩方式,分为TYPE_QUALITY、TYPE_PIXEL、TYPE_PIXEL_AND_QUALITY,慎用单独的TYPE_QUALITY(很容易OOM)!
.comPressType(CompressConfig.TYPE_PIXEL)
//目标长边像素,对TYPE_PIXEL有效(eg:原图分辨率:7952 X 5304,压缩后7952最终会小于1280)
.maxPixel(1280)
//目标大小200kb之内,对TYPE_QUALITY有效
.targetSize(200 * 1024)
.format(Bitmap.CompressFormat.WEBP, Bitmap.Config.ARGB_8888) //压缩配置
.outputDir("storage/emulated/0/DCIM/image_compressed/") //输出目录
.build();
//或者一句话CompressConfig compressConfig=CompressConfig.getDefault();
//添加须要压缩的图片路径
List<String> images = new ArrayList<>();
for (File file1 : files) {
String path = file1.getAbsolutePath();
SystemOut.println("ImageCompressor ===> image,path=" + path);
images.add(path);
}
ImageCompressManager.builder()
.paths(images)
.config(compressConfig)
.listener(new ImageCompressListener() {
@Override
public void onStart() {
SystemOut.println("ImageCompressor ===>开始压缩");
}
@Override
public void onSuccess(List<ImageInstance> images) {
SystemOut.println("ImageCompressor ===>压缩成功");
}
@Override
public void onFail(boolean allOrSingle, List<ImageInstance> images, CompressException e) {
SystemOut.println("ImageCompressor ===>压缩失败,isAll=" + allOrSingle);
}
})
.compress();
复制代码
左侧原图大小,右侧压缩后大小git
用时(基于mumu模拟器环境): github
我只开了3个核心线程,最大5个线程(PS:这玩意开多少彻底看项目需求) 多线程
经过java内置的Observaable与Observer实现简单的观察者模式
当ImageCompressManager中数据更新时,Compressor压缩工具类会收到通知,通知开启多线程执行压缩任务框架
ImageCompressManager类中: maven
Compressor类中: ide
其实网上关于图片压缩的代码一搜一大堆,基本上都差很少,包括很热门的 luban框架。 写这个框架的初衷无非就是luban可配置项太少,致使不能达到我本身的需求,因此干脆从新写一个。
Android图片压缩提及来大致有三种手段,1. 采样压缩 2.质量压缩 3. 使用libjpeg
先说说libjpeg,使用libjpeg进行压缩操做须要引入相应的so包,这就会致使包体的增长,对于绝大部分项目来讲,咱们的图片压缩要求并无那么严格。因此很显然,用这种方法就显得得不偿失了。最终 MasterImageCompress 仍是采用了前两种方法,而且将选择权交给开发者手上,能够单独使用其中一个,也能够两种混用。工具
BitmapFactory.Options options = new BitmapFactory.Options();
//计算采样率只须要宽高
options.inJustDecodeBounds = true;
//此处在option中已取得宽高
BitmapFactory.decodeFile(imageInstance.getInputPath(), options);
//计算并设置采样率
options.inSampleSize = calculateSampleSize(options, compressConfig);
//从新设置为decode整张图片,准备压缩
options.inJustDecodeBounds = false;
//应用新配置
Bitmap bitmap = BitmapFactory.decodeFile(imageInstance.getInputPath(), options);
复制代码
说白了,采样压缩就是根据图片宽高合理的计算一个缩放比(采样率),而后经过这个缩放比去忽略一部分像素点来达到压缩图片的目的。可是因为Bitmap糟糕的内存占用,因此一般获取图片的宽高时,咱们会经过 inJustDecodeBounds来控制取边仍是取整张图,那么为何这个属性能够作到只取边呢,咱们看一下源码中的注释: gradle
/** * 质量压缩 */
private void compressQuality(ImageInstance imageInstance, CompressConfig compressConfig) {
SystemOut.println("ImageCompressor ===>compressQuality()");
Bitmap inputBitmap = BitmapFactory.decodeFile(imageInstance.getInputPath());
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//上来就先压90%
int quality = 90;
inputBitmap.compress(compressConfig.getComPressFormat(), quality, byteArrayOutputStream);
//若是压缩后图片仍是>targetSize,则继续压缩(整个过程是在线程池中开了线程处理的)
while (byteArrayOutputStream.toByteArray().length > compressConfig.getTargetSize()) {
byteArrayOutputStream.reset();
quality -= 10;
if (quality <= 10) {//为了缩短压缩次数,节约你们时间,每次质量比上次减小10%
quality = 5;//限制最低压缩到5
}
inputBitmap.compress(compressConfig.getComPressFormat(), quality, byteArrayOutputStream);
if (quality == 5) {
//压缩结束
inputBitmap.recycle();
break;
}
}
}
复制代码
ok~到此结束