美文网首页Android相关
Android 图片压缩 Luban 的 RxJava2.0 版

Android 图片压缩 Luban 的 RxJava2.0 版

作者: 橙一升 | 来源:发表于2017-05-26 14:12 被阅读613次

    前言

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

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

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

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

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

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

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

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

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

    让 Luban 适用 Rxjava2.0+

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

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

    原Luban.java 使用的 Rxjava1.0+的调用方式

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

    重写之后

    当然这样子,也是有不足之处的,就是后面原作者对算法进行升级之类的,我们很难快速的升级。但是如果原作者对 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 版

        本文链接:https://www.haomeiwen.com/subject/kjatfxtx.html