1、简介:
Android让人头疼的OOM,造成OOM的原因之一就是图片,现在的手机像素越来越高,随便一张图片都是好几M,甚至几十M,这样的照片加载到app,可想而知,随便加载几张图片,手机内存就不够用了,自然而然就造成了OOM,所以,Android的图片压缩异常重要。这里,我推荐一款开源框架——Luban
2、效果与对比
这里就不放效果图了,我拷贝了鲁班github上面的介绍——Android图片压缩工具,仿微信朋友圈压缩策略,因为是逆向推算,效果还没法跟微信一模一样,但是已经很接近微信朋友圈压缩后的效果,具体看以下对比!
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()
}