Android 图片压缩 Luban 的 RxJava2.0 版本

前言

在一次偶然的情况下,在上看到 一句代码搞定 Android 图片压缩

真的是打瞌睡碰到了枕头啊~

因为最近项目开发中要新增一个模块,主要是用于上传照片的。现在的手机,像素越做越高,分辨率越来越牛逼,但是特么的体积特越来越大了,懂不懂就是4~5M 的大小。在天朝的网络环境下,上行跟下行的带宽极度的不对称,一天20M 的光纤上行却只有200k/s 左右,所以对于需要上传照片的需求来说,图片压缩是一个不可避免的议题。

一开始我对于图片的处理都是仅限于裁剪,降低分辨率,采样等常规的方法,体积是降了一些,但是图片的清晰度也跟着下来了。虽说压缩是必须的,但是让用户看清楚照片内容同样也是刚需。往往平衡两者之后,体积就没有减少的那么的明显了。

Luban 却做到了兼顾两者的同时将体积降到了很小。

github 地址:https://github.com/Curzibn/Luban

Android 图片压缩 Luban 的 RxJava2.0 版本_第1张图片

具体的使用方法可以看看原作者的 或者 GitHub

但是这个库让我有点小小遗憾的是:使用的 Rxjava 1.0+的版本


Android 图片压缩 Luban 的 RxJava2.0 版本_第2张图片

Rxjava是一个很优秀的开源库,Rxjava2.0+更是一个更更优秀的开源库,我之前就已经在项目中使用了。但是 Luban却只适合 Rxjava1.0+的版本,原作者也没有进行相关的升级适配。本着自己动手也要用上 Luban的想法(真的蛮喜欢 Luban,谢谢原作者的分享),于是自己进行了相关的改造

让 Luban 适用 Rxjava2.0+

在 github上我们可以看到,Luban 的核心文件其实就只有三个

Android 图片压缩 Luban 的 RxJava2.0 版本_第3张图片

既然要对 Luban 进行修改,那么自己 compile 就显然不合适了,而且核心文件其实也就三个,所以我们可以先把这三个文件下载下来放到自己的项目中去。然后仔细看一下最核心的文件 Luan.java ,我们发现,其实 Luban 使用 Rxjava 主要是用来控制线程,所以我们要改动的就是把这几个地方用 Rxjava2.0+的方式重写一遍就好了

Android 图片压缩 Luban 的 RxJava2.0 版本_第4张图片
原Luban.java 使用的 Rxjava1.0+的调用方式

我们把这几个地方用 Rxjava2.0+的方式重写一遍


Android 图片压缩 Luban 的 RxJava2.0 版本_第5张图片
重写之后

当然这样子,也是有不足之处的,就是后面原作者对算法进行升级之类的,我们很难快速的升级。但是如果原作者对 Luban 进行升级,很可能就直接支持 Rxjava2.0+了。

哈哈,我也在原作者的下提了适配 Rxjava2.0+,希望作者能采纳哈`

如果这篇文章又帮到你的话,请点一下‘喜欢’,我会更努力的创作的

以下还是改写之后整个 Luban.java 文件,有需要的直接替换旧的 Luban.java就好。



public classLuban {

private static final intFIRST_GEAR=1;

public static final intTHIRD_GEAR=3;

private static finalStringTAG="Luban";

private staticStringDEFAULT_DISK_CACHE_DIR="luban_disk_cache";

private static volatileLubanINSTANCE;

private finalFilemCacheDir;

privateOnCompressListenercompressListener;

privateFilemFile;

private intgear=THIRD_GEAR;

privateStringfilename;

privateLuban(File cacheDir) {

mCacheDir= cacheDir;

}

/**

* Returns a directory with a default name in the private cache directory of the application to use to store

* retrieved media and thumbnails.

*

*@paramcontextA context.

*@see#getPhotoCacheDir(android.content.Context, String)

*/

private static synchronizedFilegetPhotoCacheDir(Context context) {

returngetPhotoCacheDir(context,Luban.DEFAULT_DISK_CACHE_DIR);

}

/**

* Returns a directory with the given name in the private cache directory of the application to use to store

* retrieved media and thumbnails.

*

*@paramcontextA context.

*@paramcacheNameThe name of the subdirectory in which to store the cache.

*@see#getPhotoCacheDir(android.content.Context)

*/

private staticFilegetPhotoCacheDir(Context context,String cacheName) {

File cacheDir = context.getCacheDir();

if(cacheDir !=null) {

File result =newFile(cacheDir,cacheName);

if(!result.mkdirs() && (!result.exists() || !result.isDirectory())) {

// File wasn't able to create a directory, or the result exists but not a directory

return null;

}

File noMedia =newFile(cacheDir +"/.nomedia");

if(!noMedia.mkdirs() && (!noMedia.exists() || !noMedia.isDirectory())) {

return null;

}

returnresult;

}

if(Log.isLoggable(TAG,Log.ERROR)) {

Log.e(TAG,"default disk cache dir is null");

}

return null;

}

public staticLubanget(Context context) {

if(INSTANCE==null)INSTANCE=newLuban(Luban.getPhotoCacheDir(context));

returnINSTANCE;

}

publicLubanlaunch() {

checkNotNull(mFile,"the image file cannot be null, please call .load() before this method!");

if(compressListener!=null)compressListener.onStart();

if(gear== Luban.FIRST_GEAR)

Observable.just(mFile)

.map(newFunction() {

@Override

publicFileapply(@io.reactivex.annotations.NonNullFile file)throwsException {

returnfirstCompress(file);

}

})

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.doOnError(newConsumer() {

@Override

public voidaccept(@io.reactivex.annotations.NonNullThrowable throwable)throwsException {

if(compressListener!=null)compressListener.onError(throwable);

}

})

.onErrorResumeNext(Observable.empty())

.subscribe(newConsumer() {

@Override

public voidaccept(@io.reactivex.annotations.NonNullFile file)throwsException {

if(compressListener!=null)compressListener.onSuccess(file);

}

});

else if(gear== Luban.THIRD_GEAR)

Observable.just(mFile)

.map(newFunction() {

@Override

publicFileapply(@io.reactivex.annotations.NonNullFile file)throwsException {

returnthirdCompress(file);

}

})

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.doOnError(newConsumer() {

@Override

public voidaccept(@io.reactivex.annotations.NonNullThrowable throwable)throwsException {

if(compressListener!=null)compressListener.onError(throwable);

}

})

.onErrorResumeNext(Observable.empty())

.subscribe(newConsumer() {

@Override

public voidaccept(@io.reactivex.annotations.NonNullFile file)throwsException {

if(compressListener!=null)compressListener.onSuccess(file);

}

})

;

return this;

}

publicLubanload(File file) {

mFile= file;

return this;

}

publicLubansetCompressListener(OnCompressListener listener) {

compressListener= listener;

return this;

}

publicLubanputGear(intgear) {

this.gear= gear;

return this;

}

/**

*@deprecated

*/

publicLubansetFilename(String filename) {

this.filename= filename;

return this;

}

publicObservableasObservable() {

if(gear==FIRST_GEAR)

returnObservable.just(mFile).map(newFunction() {

@Override

publicFileapply(@io.reactivex.annotations.NonNullFile file)throwsException {

returnfirstCompress(file);

}

});

else if(gear==THIRD_GEAR)

returnObservable.just(mFile).map(newFunction() {

@Override

publicFileapply(@io.reactivex.annotations.NonNullFile file)throwsException {

returnthirdCompress(file);

}

});

else returnObservable.empty();

}

privateFilethirdCompress(@NonNullFile file) {

String thumb =mCacheDir.getAbsolutePath() + File.separator+

(TextUtils.isEmpty(filename) ? System.currentTimeMillis() :filename) +".jpg";

doublesize;

String filePath = file.getAbsolutePath();

intangle = getImageSpinAngle(filePath);

intwidth = getImageSize(filePath)[0];

intheight = getImageSize(filePath)[1];

intthumbW = width %2==1? width +1: width;

intthumbH = height %2==1? height +1: height;

width = thumbW > thumbH ? thumbH : thumbW;

height = thumbW > thumbH ? thumbW : thumbH;

doublescale = ((double) width / height);

if(scale <=1&& scale >0.5625) {

if(height <1664) {

if(file.length() /1024<150)returnfile;

size = (width * height) / Math.pow(1664,2) *150;

size = size <60?60: size;

}else if(height >=1664&& height <4990) {

thumbW = width /2;

thumbH = height /2;

size = (thumbW * thumbH) / Math.pow(2495,2) *300;

size = size <60?60: size;

}else if(height >=4990&& height <10240) {

thumbW = width /4;

thumbH = height /4;

size = (thumbW * thumbH) / Math.pow(2560,2) *300;

size = size <100?100: size;

}else{

intmultiple = height /1280==0?1: height /1280;

thumbW = width / multiple;

thumbH = height / multiple;

size = (thumbW * thumbH) / Math.pow(2560,2) *300;

size = size <100?100: size;

}

}else if(scale <=0.5625&& scale >0.5) {

if(height <1280&& file.length() /1024<200)returnfile;

intmultiple = height /1280==0?1: height /1280;

thumbW = width / multiple;

thumbH = height / multiple;

size = (thumbW * thumbH) / (1440.0*2560.0) *400;

size = size <100?100: size;

}else{

intmultiple = (int) Math.ceil(height / (1280.0/ scale));

thumbW = width / multiple;

thumbH = height / multiple;

size = ((thumbW * thumbH) / (1280.0* (1280/ scale))) *500;

size = size <100?100: size;

}

returncompress(filePath,thumb,thumbW,thumbH,angle,(long) size);

}

privateFilefirstCompress(@NonNullFile file) {

intminSize =60;

intlongSide =720;

intshortSide =1280;

String filePath = file.getAbsolutePath();

String thumbFilePath =mCacheDir.getAbsolutePath() + File.separator+

(TextUtils.isEmpty(filename) ? System.currentTimeMillis() :filename) +".jpg";

longsize =0;

longmaxSize = file.length() /5;

intangle = getImageSpinAngle(filePath);

int[] imgSize = getImageSize(filePath);

intwidth =0,height =0;

if(imgSize[0] <= imgSize[1]) {

doublescale = (double) imgSize[0] / (double) imgSize[1];

if(scale <=1.0&& scale >0.5625) {

width = imgSize[0] > shortSide ? shortSide : imgSize[0];

height = width * imgSize[1] / imgSize[0];

size = minSize;

}else if(scale <=0.5625) {

height = imgSize[1] > longSide ? longSide : imgSize[1];

width = height * imgSize[0] / imgSize[1];

size = maxSize;

}

}else{

doublescale = (double) imgSize[1] / (double) imgSize[0];

if(scale <=1.0&& scale >0.5625) {

height = imgSize[1] > shortSide ? shortSide : imgSize[1];

width = height * imgSize[0] / imgSize[1];

size = minSize;

}else if(scale <=0.5625) {

width = imgSize[0] > longSide ? longSide : imgSize[0];

height = width * imgSize[1] / imgSize[0];

size = maxSize;

}

}

returncompress(filePath,thumbFilePath,width,height,angle,size);

}

/**

* obtain the image's width and height

*

*@paramimagePaththe path of image

*/

public int[]getImageSize(String imagePath) {

int[] res =new int[2];

BitmapFactory.Options options =newBitmapFactory.Options();

options.inJustDecodeBounds=true;

options.inSampleSize=1;

BitmapFactory.decodeFile(imagePath,options);

res[0] = options.outWidth;

res[1] = options.outHeight;

returnres;

}

/**

* obtain the thumbnail that specify the size

*

*@paramimagePaththe target image path

*@paramwidththe width of thumbnail

*@paramheightthe height of thumbnail

*@return{@linkBitmap}

*/

privateBitmapcompress(String imagePath, intwidth, intheight) {

BitmapFactory.Options options =newBitmapFactory.Options();

options.inJustDecodeBounds=true;

BitmapFactory.decodeFile(imagePath,options);

intoutH = options.outHeight;

intoutW = options.outWidth;

intinSampleSize =1;

if(outH > height || outW > width) {

inthalfH = outH /2;

inthalfW = outW /2;

while((halfH / inSampleSize) > height && (halfW / inSampleSize) > width) {

inSampleSize *=2;

}

}

options.inSampleSize= inSampleSize;

options.inJustDecodeBounds=false;

intheightRatio = (int) Math.ceil(options.outHeight/ (float) height);

intwidthRatio = (int) Math.ceil(options.outWidth/ (float) width);

if(heightRatio >1|| widthRatio >1) {

if(heightRatio > widthRatio) {

options.inSampleSize= heightRatio;

}else{

options.inSampleSize= widthRatio;

}

}

options.inJustDecodeBounds=false;

returnBitmapFactory.decodeFile(imagePath,options);

}

/**

* obtain the image rotation angle

*

*@parampathpath of target image

*/

private intgetImageSpinAngle(String path) {

intdegree =0;

try{

ExifInterface exifInterface =newExifInterface(path);

intorientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL);

switch(orientation) {

caseExifInterface.ORIENTATION_ROTATE_90:

degree =90;

break;

caseExifInterface.ORIENTATION_ROTATE_180:

degree =180;

break;

caseExifInterface.ORIENTATION_ROTATE_270:

degree =270;

break;

}

}catch(IOException e) {

e.printStackTrace();

}

returndegree;

}

/**

* 指定参数压缩图片

* create the thumbnail with the true rotate angle

*

*@paramlargeImagePaththe big image path

*@paramthumbFilePaththe thumbnail path

*@paramwidthwidth of thumbnail

*@paramheightheight of thumbnail

*@paramanglerotation angle of thumbnail

*@paramsizethe file size of image

*/

privateFilecompress(String largeImagePath,String thumbFilePath, intwidth, intheight, intangle, longsize) {

Bitmap thbBitmap = compress(largeImagePath,width,height);

thbBitmap =rotatingImage(angle,thbBitmap);

returnsaveImage(thumbFilePath,thbBitmap,size);

}

/**

* 旋转图片

* rotate the image with specified angle

*

*@paramanglethe angle will be rotating 旋转的角度

*@parambitmaptarget image              目标图片

*/

private staticBitmaprotatingImage(intangle,Bitmap bitmap) {

//rotate image

Matrix matrix =newMatrix();

matrix.postRotate(angle);

//create a new image

returnBitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix, true);

}

/**

* 保存图片到指定路径

* Save image with specified size

*

*@paramfilePaththe image file save path 储存路径

*@parambitmapthe image what be save  目标图片

*@paramsizethe file size of image  期望大小

*/

privateFilesaveImage(String filePath,Bitmap bitmap, longsize) {

checkNotNull(bitmap,TAG+"bitmap cannot be null");

File result =newFile(filePath.substring(0,filePath.lastIndexOf("/")));

if(!result.exists() && !result.mkdirs())return null;

ByteArrayOutputStream stream =newByteArrayOutputStream();

intoptions =100;

bitmap.compress(Bitmap.CompressFormat.JPEG,options,stream);

while(stream.toByteArray().length/1024> size && options >6) {

stream.reset();

options -=6;

bitmap.compress(Bitmap.CompressFormat.JPEG,options,stream);

}

bitmap.recycle();

try{

FileOutputStream fos =newFileOutputStream(filePath);

fos.write(stream.toByteArray());

fos.flush();

fos.close();

stream.close();

}catch(IOException e) {

e.printStackTrace();

}

return newFile(filePath);

}

}

你可能感兴趣的:(Android 图片压缩 Luban 的 RxJava2.0 版本)