1.背景 对于网络传输照片和文件大小肯定是越小越好 这点沙滩康师傅也是做了些后台判断 图片小于500k才提供上传 (亲测gif 将近3M也能传 应该是后台接口没限制gif后缀文件) 但是为了服务器压力 还是尽量压缩下图和动图吧
2.起因和查阅资料 因为本来想发gif动态图进行展示的 发现原先压缩图片的代码对于gif不生效 有查阅了一些资料 博客和掘金等网站都是好几年前的水wen 帮助不大 (可以说10篇9篇广告 剩下一篇还是图片压缩) 周日无聊拿着手机掘金APP一搜 就搜到了一篇文章 不过博主没有进行很详细的解析 贴出了github地址和使用方法
3.原博贴:Android压缩GIF格式图片
4.在我简化实操 最终生成以下代码 可以说每张gif可以减少最少一半的内存 (其实原理很简单 就是单纯抽帧 保留多少帧后将帧再写入文件内 就完成了)
github 引用 第三方库gifFile https://github.com/koral--/android-gif-drawable 一个实现类 https://github.com/nbadal/android-gif-encoder
首先需要能够获取gif的路径 这一步自行解决啦 代码如下
/**
* 先把GIF分解成bitmap,进行减帧操作,最后合成一张新的GIF
* 1.根据文件指定文件长度进行压缩
* @param gifFile gif文件
* @param targetLength 压缩的目标体积
* @param savePath 压缩后的保存路径
*/
public static String compressFrame(File gifFile, long targetLength, String savePath) {
GifDrawable gifDrawable = null;
try {
long gifFileLength = gifFile.length()/1024;
SLog.e("compressFrame file gifFileLength = "+gifFileLength);
gifDrawable = new pl.droidsonroids.gif.GifDrawable(gifFile);
if (gifFileLength > targetLength) {
// 计算压缩比例 例如是2 每2张取1张 3 4 5 6 同理
int mold = (int) Math.ceil(gifFileLength * 1.0 / targetLength) +1;
SLog.e("compressFrame file mold = "+mold);
return compressFrameGif(gifDrawable , mold ,savePath);
}
} catch (Exception e) {
SLog.e("compressFrameGif e = "+e.toString());
} finally {
if (gifDrawable != null) {
gifDrawable.recycle();
}
}
return savePath;
}
/**
* 先把GIF分解成bitmap,进行减帧操作,最后合成一张新的GIF
* 2.根据指定帧数进行压缩
* @param gifFile gif文件
* @param maxFrames 最大帧数
* @param savePath 压缩后的保存路径
*/
public static String compressFrame(File gifFile, int maxFrames, String savePath) {
GifDrawable gifDrawable = null;
try {
gifDrawable = new GifDrawable(gifFile);
int gifFrames = gifDrawable.getNumberOfFrames();
if ( gifFrames > maxFrames){
// 计算压缩比例 例如是2 每2张取1张 3 4 5 6 同理
int mold = (int) (Math.ceil(gifFrames * 1.0 / maxFrames)) + 1;
return compressFrameGif(gifDrawable, mold, savePath);
}
} catch (Exception e){
SLog.e("compressFrameGif e = "+e.toString());
} finally {
if (gifDrawable != null){
gifDrawable.recycle();
}
}
return savePath;
}
// 写入保存路径 抽离方法
private static String compressFrameGif(GifDrawable gifDrawable ,int mold ,String savePath){
ByteArrayOutputStream bos = null;
FileOutputStream outStream = null;
try {
int gifFrames = gifDrawable.getNumberOfFrames();
int gifDuration = gifDrawable.getDuration();
bos = new ByteArrayOutputStream();
AnimatedGifEncoder encoder = new AnimatedGifEncoder();
encoder.setRepeat(0);
encoder.start(bos);
encoder.setDelay(gifDuration / gifFrames);
// 核心内容 抽帧 并添加帧进入文件类
for (int i = 0; i < gifFrames; i++) {
// 这里需要各自去定制 本质上抽出来的帧数的大小都会比原图帧的大 如何缩减达到减少内存的效果
if (i % mold == 0) {
encoder.addFrame(gifDrawable.seekToFrameAndGet(i));
}
}
encoder.finish();
outStream = new FileOutputStream(savePath);
outStream.write(bos.toByteArray());
} catch (Exception e){
SLog.e("compressFrameGif e = "+e.toString());
} finally {
try {
if (bos != null) {
bos.close();
}
if (outStream != null) {
outStream.close();
}
return savePath;
} catch (Exception e) {
e.printStackTrace();
}
}
return savePath;
}
如何使用 涉及IO写入 开启线程 这里使用的是Rxjava
public static final String SLASH = "/";
/**
* 压缩GIF
*/
private void compressionGif(String path ) {
// 转GIF
String directoryPrefix = context.getExternalFilesDir("").getAbsolutePath()+ImageCompressUtils.SLASH;
String imagepath = directoryPrefix +"sugar"+ ImageCompressUtils.SLASH + "image"+ ImageCompressUtils.SLASH;
File file = new File(imagepath);
if (!file.exists()) {
file.mkdirs();
}
Observable<String> observable = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(@io.reactivex.annotations.NonNull ObservableEmitter<String> e) throws Exception {
File compressImageFile = null;
compressImageFile = File.createTempFile("image", ".gif", file);
String s = ImageCompressUtils.compressFrame(new File(path), 24, compressImageFile.getAbsolutePath());
// s = ImageCompressUtils.compressFrame(new File(path), 500l, compressImageFile.getAbsolutePath());
e.onNext(s);
e.onComplete();
}
});
observable.subscribeOn(Schedulers.io());
observable.observeOn(AndroidSchedulers.mainThread());
observable.subscribe(new Observer<String>() {
@Override
public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d){
}
@Override
public void onNext(@io.reactivex.annotations.NonNull String compath) {
if(!TextUtils.isEmpty(compath)){
pic_file = new File(compath);
// 上传操作或者进行Glide显示都可
}
}
@Override
public void onError(@io.reactivex.annotations.NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
}
压缩图片的代码挺多的 这gif说是压缩 其实减少帧的数量达到大小减小 但是损坏了画质 或者应该说是流畅度 对于原图质量要求高的要谨慎使用 有问题沙滩联系啦 !! 内卷起来吧 滩友们!