Android原生的压缩方法,不在乎两种:通过设置simpleSize根据图片尺寸压缩图片;通过Bitmap.compress方法通过压缩图片质量,去压缩。但是我们当我们对图片质量和图片文件大小同时存在要求时,我们发现无论怎么去设置参数,我们所做的效果总是不能尽如人意,而且同样大小的图片,效果却总是比iOS的效果差很多。

其实归根到底的是iOS和Android的压缩算法存在一些差异。

Android所用的是skia的压缩算法,它在google的很多地方,比如chrome、Android等都有使用,而Bitmap的压缩算法就是通过这个实现。而Skia是libjpeg进行了封装,google在实现skia时对其中一个地方进行了修改:通过哈夫曼算法来进行图片压缩,但是采用这个算法的时候,机器可能会出现性能问题,于是并没有调动这个方法。

boolean optimize_coding 
TRUE causes the compressor to compute optimal Huffman coding tables 
for the image. This requires an extra pass over the data and 
therefore costs a good deal of space and time. The default is 
FALSE, which tells the compressor to use the supplied or default 
Huffman tables. In most cases optimal tables save only a few percent 
of file size compared to the default tables. Note that when this is 
TRUE, you need not supply Huffman tables at all, and any you do 
supply will be overwritten.

但是,这个问题在十年前或许存在,但是现在的机器已经完全可以解决性能问题,而google却忘记了更正当年的代码。


接下来,我们就自己去编译生成libjpeg的动态库,然后调用。

github中android libjpeg的源文件地址:https://github.com/libjpeg-turbo/libjpeg-turbo

这个我们需要自己去编译,但是已经有人帮我们编译好了,压缩算法也已经实现,因此,我们去下载然后编译即可:https://github.com/bither/bither-android-lib


首先将上面下载好的已经编译好的libjpeg放到jni目录下,将下图内容都放到jni目录中:

android 图片压缩到指定大小 android 图片压缩工具类_图片

安装好ndk以后,直接输入ndk-build即可。

接下来就会编译生成arm下的动态库,使用的时候必须在项目中新建一个包net.bither.util,然后加入下面这个类方法,也就是使用了libjpeg开启哈夫曼算法的压缩算法:


/*
 * Copyright 2014 http://Bither.net
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.bither.util;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.Log;

public class NativeUtil {
	private static int DEFAULT_QUALITY = 95;

	public static void compressBitmap(Bitmap bit, String fileName,
			boolean optimize) {
		compressBitmap(bit, DEFAULT_QUALITY, fileName, optimize);

	}

	public static void compressBitmap(Bitmap bit, int quality, String fileName,
			boolean optimize) {
		Log.d("native", "compress of native");

		// if (bit.getConfig() != Config.ARGB_8888) {
		Bitmap result = null;

		result = Bitmap.createBitmap(bit.getWidth() / 3, bit.getHeight() / 3,
				Config.ARGB_8888);// 缩小3倍
		Canvas canvas = new Canvas(result);
		Rect rect = new Rect(0, 0, bit.getWidth(), bit.getHeight());// original
		rect = new Rect(0, 0, bit.getWidth() / 3, bit.getHeight() / 3);// 缩小3倍
		canvas.drawBitmap(bit, null, rect, null);
		saveBitmap(result, quality, fileName, optimize);
		result.recycle();
		// } else {
		// saveBitmap(bit, quality, fileName, optimize);
		// }

	}

	private static void saveBitmap(Bitmap bit, int quality, String fileName,
			boolean optimize) {

		compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality,
				fileName.getBytes(), optimize);

	}

	private static native String compressBitmap(Bitmap bit, int w, int h,
			int quality, byte[] fileNameBytes, boolean optimize);

	static {
		System.loadLibrary("jpegbither");
		System.loadLibrary("bitherjni");

	}

}



注意包名和方法名都是不能变的,因为在编译的时候已经被确定。

如果我们想要去修改方法名放入自己的项目中怎么办。那我们就需要去修改一下bitherlibjni.c这个文件。

例如我想把这个方法放在com.example.test中的ImageUtils中,

我们只需要把c文件中的


jstring Java_net_bither_util_NativeUtil_compressBitmap(JNIEnv* env,
      jobject thiz, jobject bitmapcolor, int w, int h, int quality,
      jbyteArray fileNameStr, jboolean optimize) {

修改为


jstring Java_com_example_test_ImageUtils_compressBitmap(JNIEnv* env,
      jobject thiz, jobject bitmapcolor, int w, int h, int quality,
      jbyteArray fileNameStr, jboolean optimize) {

这个对会ndk开发的同学应该都知道,接下来我们重新运行ndk-build就可以重新替换so文件然后调用我们自己的libjpeg了。

但是,目前libjpeg是很多年前的了。github上这个库只支持arm架构的cpu,如果我们想用这个库的话,只能通过在加载so文件的时候对其进行trycatch处理,来防止x86等其他cpu架构的机器加载so文件报错。


压缩效果对比:

原图:

android 图片压缩到指定大小 android 图片压缩工具类_压缩_02

android 图片压缩到指定大小 android 图片压缩工具类_libjpeg_03

bitmap压缩:

android 图片压缩到指定大小 android 图片压缩工具类_压缩_04

android 图片压缩到指定大小 android 图片压缩工具类_压缩_05


libjpeg压缩:

android 图片压缩到指定大小 android 图片压缩工具类_图片_06

android 图片压缩到指定大小 android 图片压缩工具类_图片_07


github下载地址:https://github.com/xiaoqiAndroid/LibJpegCompress/tree/master