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~到此結束