Android 图片压缩-鲁班压缩

285 阅读2分钟

1、简介:

Android让人头疼的OOM,造成OOM的原因之一就是图片,现在的手机像素越来越高,随便一张图片都是好几M,甚至几十M,这样的照片加载到app,可想而知,随便加载几张图片,手机内存就不够用了,自然而然就造成了OOM,所以,Android的图片压缩异常重要。这里,我推荐一款开源框架——Luban

2、效果与对比

  这里就不放效果图了,我拷贝了鲁班github上面的介绍——Android图片压缩工具,仿微信朋友圈压缩策略,因为是逆向推算,效果还没法跟微信一模一样,但是已经很接近微信朋友圈压缩后的效果,具体看以下对比!

2733c712bd563be86bf7d7722da71ae.png

3、导入

implementation 'top.zibin:Luban:1.1.8'

4、使用方法

方法描述
load传入原图
filter设置开启压缩条件
ignoreBy不压缩的阈值,单位为K
setFocusAlpha设置是否保留透明通道
setTargetDir缓存压缩图片路径
setCompressListener压缩回调接口
setRenameListener压缩前重命名接口

5、异步调用

Luban内部采用IO线程进行图片压缩,外部调用只需设置好结果监听即可:

Luban.with(this)
        .load(photos)
        .ignoreBy(100)
        .setTargetDir(getPath())
        .filter(new CompressionPredicate() {
          @Override
          public boolean apply(String path) {
            return !(TextUtils.isEmpty(path) || path.toLowerCase().endsWith(".gif"));
          }
        })
        .setCompressListener(new OnCompressListener() {
          @Override
          public void onStart() {
            // TODO 压缩开始前调用,可以在方法内启动 loading UI
          }

          @Override
          public void onSuccess(File file) {
            // TODO 压缩成功后调用,返回压缩后的图片文件
          }

          @Override
          public void onError(Throwable e) {
            // TODO 当压缩过程出现问题时调用
          }
        }).launch();
fun getPath(): String {
    val path = FileUtils.getApkFilePath(AsuraApplication.instance, "") + "/Luban/image/"
    val file = File(path)
    return if (file.mkdirs()) {
        path
    } else path
}
/**
 * 获取app本地的路径
 *
 * @param context
 * @param downLoadUrl
 * @return
 */
public static String getApkFilePath(Context context, String downLoadUrl) {
    if (Environment.getExternalStorageState().equals(
            Environment.MEDIA_MOUNTED)) {
        File external = AsuraApplication.Companion.getInstance().getExternalFilesDir(null);
        if (external != null) {
            return external.getAbsolutePath();
        }
    }
    return AsuraApplication.Companion.getInstance().getFilesDir().getAbsolutePath();
}

6、同步调用

同步方法请尽量避免在主线程调用以免阻塞主线程,下面以rxJava调用为例

Flowable.just(photos)
    .observeOn(Schedulers.io())
    .map(new Function<List<String>, List<File>>() {
      @Override public List<File> apply(@NonNull List<String> list) throws Exception {
        // 同步方法直接返回压缩后的文件
        return Luban.with(MainActivity.this).load(list).get();
      }
    })
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe();

7、使用:

/**
 * 单个递归上传图片
 */
fun singlePic(
    mContext: Activity, currentUrl: String, imageList: ArrayList<String>
) {
    //图片的个数
    var picSize = imageList.size

    var oldFile = File(currentUrl)
    val originSize: IntArray = computeSize(oldFile)
    val originArg = String.format(
        Locale.CHINA,
        "压缩前参数:%d*%d, %dk",
        originSize.get(0),
        originSize.get(1),
        oldFile.length() shr 10
    )
    LogUtils.debugInfo("111-----: ${originArg}")
    //本地路径
    var localPath = getPath()
    //LogUtils.debugInfo("localPath-----: ${localPath}")
    Luban.with(mContext)
        .load(currentUrl)
        .ignoreBy(100)
        .setFocusAlpha(false)//设置是否保留透明通道
        .setTargetDir(localPath)
        .filter { path ->
            !(TextUtils.isEmpty(path) || path.lowercase(Locale.getDefault())
                .endsWith(".gif"))
        }
        .setCompressListener(object : OnCompressListener {
            override fun onStart() {
                //
                LogUtils.debugInfo("-----开始压缩------")
            }

            override fun onSuccess(file: File) {
                //
                LogUtils.debugInfo("-----压缩完成------")
                var newUrl = file.absolutePath ?: ""
                val originSize: IntArray = computeSize(file)
                val thumbArg = String.format(
                    Locale.CHINA,
                    "压缩后参数:%d*%d, %dk",
                    originSize.get(0),
                    originSize.get(1),
                    file.length() shr 10
                )
                LogUtils.debugInfo("222-----: ${thumbArg}")
                if (!TextUtils.isEmpty(newUrl)) {
                    var waterMark = CommonUtils.photoWatermark(mContext)
                    ImageUtil.addWaterMark(
                        mContext, newUrl, waterMark
                    )
                    //上传图片到网上
                    uploadNetPic(mContext, newUrl) {
                        //上传图片
                        ++CommonPhotoUtils.uploadPicNum
                        if (it) {
                            //上传成功
                            ++CommonPhotoUtils.uploadSuccessPicNum
                        } else {
                            //上传失败
                            ++CommonPhotoUtils.uploadFailPicNum
                        }

                        if (CommonPhotoUtils.uploadPicNum < picSize) {
                            var currentUrl2 = imageList.get(CommonPhotoUtils.uploadPicNum)
                            //继续上传图片
                            singlePic(mContext, currentUrl2, imageList)
                        } else {
                            if (CommonPhotoUtils.uploadFailPicNum == 0) {
                                ToastUtils.showToast(mContext, "图片上传成功")
                            } else {
                                ToastUtils.showToast(
                                    mContext,
                                    "上传成功:${CommonPhotoUtils.uploadSuccessPicNum}张, 上传失败:${CommonPhotoUtils.uploadFailPicNum}张"
                                )
                            }
                        }
                    }
                }
            }

            override fun onError(e: Throwable) {
                //
                LogUtils.debugInfo("-----压缩失败------:${e.toString()}")
            }
        }).launch()


}