项目结构图

结构图
MainActivity
public class MainActivity extends Activity {
    static {
        System.loadLibrary("jpegbither");
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 申请权限
        checkPermission(this);
    }
    private boolean checkPermission(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(
                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            activity.requestPermissions(new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            }, 1);
        }
        return false;
    }
    // 按钮点击事件
    public void onLoad(View view) {
        File input = new File(Environment.getExternalStorageDirectory(), "before.jpg");
        Bitmap inputBitmap = BitmapFactory.decodeFile(input.getAbsolutePath());
        File output = new File(Environment.getExternalStorageDirectory(), "哈夫曼压缩.jpg");
        // 哈夫曼压缩
        compress(inputBitmap, output.getAbsolutePath());
        // 质量压缩
        File output1 = new File(Environment.getExternalStorageDirectory(), "质量压缩.jpg");
        compressQuality(inputBitmap, output1);
        // 尺寸压缩
        File output2= new File(Environment.getExternalStorageDirectory(), "尺寸压缩.jpg");
        compressSize(inputBitmap, output2);
    }
    public native void compress(Bitmap bitmap, String path);
    /*
     *  质量压缩:设置bitmap options属性,降低图片的质量,不会减少像素
        第一个参数:需要压缩的bitmap图片对象,第二个参数:压缩后图片保存的位置
        设置options属性值(0-100)来实现压缩
     */
    public void compressQuality(Bitmap bmp, File file) {
        int options = 10;           // 100为不压缩
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 把压缩后的数据存放到baos中
        bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(baos.toByteArray());
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /*
     * 尺寸压缩:通过缩放图片像素,来减少图片占用内存大小
     */
    public void compressSize(Bitmap bmp, File file) {
        // 尺寸压缩倍数:值越大,图片尺寸越小
        int ratio = 6;
        // 压缩Bitmap到对应尺寸
        Bitmap result = Bitmap.createBitmap(bmp.getWidth() / ratio, bmp.getHeight() / ratio,
                Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(result);
        Rect rect = new Rect(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio);
        canvas.drawBitmap(bmp, null, rect, null);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 把压缩后的数据存放到baos中
        result.compress(Bitmap.CompressFormat.JPEG, 100 ,baos);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(baos.toByteArray());
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
native-lib.cpp
#include <jni.h>
#include <string>
#include <android/bitmap.h>
#include <android/log.h>
#include <malloc.h>
extern "C" {
    #include "jpeglib.h"
}
typedef uint8_t BYTE;
void writeImg(BYTE *data, const char *path, int w, int h) {
    // jpeg引擎,绕过skia来实现压缩
    //信使
    struct jpeg_compress_struct jpeg_struct;
    // 设置错误处理信息
    jpeg_error_mgr err;
    jpeg_struct.err = jpeg_std_error(&err);
    // 给结构体分配内存
    jpeg_create_compress(&jpeg_struct);
    FILE *file = fopen(path, "wb");
    // 设置输出路径
    jpeg_stdio_dest(&jpeg_struct, file);
    jpeg_struct.image_width = w;
    jpeg_struct.image_height = h;
    // 初始化  初始化
    jpeg_struct.arith_code = FALSE;         // 开启hufuman算法
    jpeg_struct.optimize_coding = TRUE;
    jpeg_struct.in_color_space = JCS_RGB;
    jpeg_struct.input_components = 3;
    // 其他的设置默认
    jpeg_set_defaults(&jpeg_struct);
    jpeg_set_quality(&jpeg_struct, 20, true);
    jpeg_start_compress(&jpeg_struct, TRUE);
    JSAMPROW row_pointer[1];
    // 一行的rgb
    int row_stride = w * 3;
    while (jpeg_struct.next_scanline < h) {
        row_pointer[0] = &data[jpeg_struct.next_scanline * w * 3];
        jpeg_write_scanlines(&jpeg_struct, row_pointer, 1);
    }
    jpeg_finish_compress(&jpeg_struct);
    jpeg_destroy_compress(&jpeg_struct);
    fclose(file);
}
extern "C" JNIEXPORT void JNICALL
Java_com_zc_pressimage_MainActivity_compress(JNIEnv *env, jobject instance, jobject bitmap, jstring path_) {
    const char *path = env->GetStringUTFChars(path_, 0);
    AndroidBitmapInfo bitmapInfo;
    AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);
    BYTE *pixels;
    AndroidBitmap_lockPixels(env, bitmap, (void **) &pixels);
    int h = bitmapInfo.height;
    int w = bitmapInfo.width;
    BYTE *data, *tmpData;
    data = (BYTE *) malloc(w * h * 3);
    tmpData = data;
    BYTE r, g, b;
    int color;
    for (int i = 0; i < h; ++i) {
        for (int j = 0; j < w; ++j) {
            color = *((int *) pixels);
            r = ((color & 0x00FF0000) >> 16);
            g = ((color & 0x0000FF00) >> 8);
            b = ((color & 0x000000FF));
            *data = b;
            *(data + 1) = g;
            *(data + 2) = r;
            data += 3;
            pixels += 4;
        }
    }
    AndroidBitmap_unlockPixels(env, bitmap);
    writeImg(tmpData, path, w, h);
    env->ReleaseStringUTFChars(path_, path);
}