Java 调用bsdiff/bspatch开源库.dll打差分包

本文详细介绍如何在Windows环境下编译bsdiff/bspatch开源库,并将其与Java应用程序集成,实现对二进制文件的高效差分与合并。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【前言】

      bsdiff/bspatch是对二进制文件进行差分与合并的开源库,这个开源库的特点就是以空间换时间,打包效率比较高,但是比较耗内存,不过现在大部分机器的内存都不是问题啦,效率高才是硬道理,我们接下来盘它!
      bsdiff/bspatch的源码地址在这里,不过这个源码是适合在linux系统编译的,因为里面依赖到linux系统下的一些api,比如:#include <unistd.h>openlseek等,这些在window系统都是找不到,为了方便,我们需要另一个修改好的适合window系统的 bsdiff/bspatch源码,这里有编译好的exe文件,可以直接用,不过我们需要给Java调用,需要把它编译成dll动态链接库才行

一、编译

1、下载安装好visual studio 2019,创建新项目,选择空项目,下一步
在这里插入图片描述
2、配置好项目名,选择好存放位置,点创建即可
在这里插入图片描述
3、接着,我们把下载好的bsdiff-win-master.zip解压,从bsdiff-win目录下拷贝bsdiff.c,从bspatch-win目录下拷贝bspatch.c,都是放到源文件目录下
在这里插入图片描述
【注意】这里有一点需要特别留意一下,拷贝时候记得先把sln解决方案视图切换为文件夹视图,拷贝完成之后再切换回去,手动添加现有项到解决方案中,当然也可以不用来回切换视图,你直接把文件拷贝到项目目录下,再直接手动添加现有项也可以
在这里插入图片描述
在这里插入图片描述

4、添加完成之后,默认情况下的话,VS本地是找不到#include <bzlib.h>,会出现红色下划线,鼠标放上去有提示让你去下载vcpkg这个工具,然后通过这个工具下载所需的依赖库
5、下载完成之后,双击运行bootstrap-vcpkg.bat,会自动生成vcpkg.exe这个文件在这里插入图片描述
6、接下来,我们可以通过这个vcpkg.exe下载bzip2,下载命令是:

 vcpkg install bzip2

但是,这个默认下载的是x86-windows版本,也就是32位window版本,现在一般都是64位的机器了,所以,我们指定下载64位的

vcpkg install bzip2:x64-windows

慢慢等待下载完成即可
7、然后,运行以下命令应用到所有项目即可

vcpkg integrate install

8、上述完成之后,#include <bzlib.h>找不到问题解决了,但是点开bsdiff.c或者bspatch.c会提示未定义标识符u_char,这个在unix系统是定义在#include<sys/types.h>头文件下,而window系统是定义在#include <winsock.h>头文件下,添加一下即可
9、然后,我们点击“本地window调试器”,运行一下,会出现以下错误:
在这里插入图片描述

 error C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS

10、上述的报错,大概是说你用的那个函数可能不安全,已经是弃用的api了,假如需要去掉这个报错就在文件头添加#define _CRT_SECURE_NO_WARNINGS,这样是可以解决这个报错的,不过我们这里使用另一种解决办法,选中项目-点击工具栏的项目-属性
在这里插入图片描述
11、由于我们是想要编译成dll的,所以我们先顺便在上面那个界面,选择常规 - 配置类型,选择动态库(.dll)即可
在这里插入图片描述
12、然后点击C/C++ - 常规 - SDL检查,设置,确定即可(注意,每次切换配置类型都需要重新配置一下这个)
在这里插入图片描述
13、重新点击“本地window调试器”运行一下,会出现以下错误:
在这里插入图片描述
14、上述错误主要是因为bspatch.c跟bsdiff.c都有main函数定义,由于对于C来说,mian函数就是执行的入口函数,是不允许有两个的,那么,我们把它分别改为bsdiff_mainbspatch_main
在这里插入图片描述

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200904103502633.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjc4ODI2,size_16,color_FFFFFF,t_70#pic_center
15、重新执行一下,发现又有出现一个新的错误:fatal error LNK1169: 找到一个或多个多重定义的符号
在这里插入图片描述
16、上述错误主要是因为本来这个项目作者是让bsdiff.c跟bspatch.c单独编译的,我们强制放一块,当然就会出现重复定义问题,那么咱们把提示重复定义的err函数提取出头文件跟实现的C文件
err.h

#pragma once
void err(int exitcode, const char* fmt, ...);

err.c

#include<stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "err.h"
void err(int exitcode, const char* fmt, ...)
{
	va_list valist;
	va_start(valist, fmt);
	vprintf(fmt, valist);
	va_end(valist);
	exit(exitcode);
}

17、选中项目,点击生成 - 重新生成解决方案,看一下日志,已经生成成功
在这里插入图片描述
18、不过单纯这样子的.dll,没啥用,Java是没法调用到C实现的函数,还需要定义JNI接口才行
1)把你jdk安装目录的jni.h依赖拷贝过来,由于jni.h还依赖到jni_md.h,在win32目录下,一起拷贝过去(记得不是解决方案视图,要切换到文件夹视图再拷贝)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200904113618970.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMjc4ODI2,size_16,color_FFFFFF,t_70#pic_center
2)重新生成解决方案看看,编译通过
在这里插入图片描述
19.接下来,咱们得写个jni对外接口类:main.c

#include <stdlib.h>
#include "jni_md.h"
#include "jni.h"

extern int bspatch_main(int argc, char *argv[]);

extern int bsdiff_main(int argc, char *argv[]);

int main_exec(int (*mainFunc)(int, char **), int argc, char **argv) {
    
    return mainFunc(argc, argv);
   
}

JNIEXPORT jboolean JNICALL
Java_com_sswl_BsDiff_make(JNIEnv *env, jclass type, jstring oldFilePath_, jstring newFilePath_,
                             jstring patchPath_) {

    const char *ch[5] = {0};
    ch[0] = "bspatch";
    ch[1] = (*env)->GetStringUTFChars(env, oldFilePath_, 0);
    ch[2] = (*env)->GetStringUTFChars(env, newFilePath_, 0);
    ch[3] = (*env)->GetStringUTFChars(env, patchPath_, 0);

    int ret = main_exec(bspatch_main, 4, ch);

    (*env)->ReleaseStringUTFChars(env, oldFilePath_, ch[1]);
    (*env)->ReleaseStringUTFChars(env, newFilePath_, ch[2]);
    (*env)->ReleaseStringUTFChars(env, patchPath_, ch[3]);

    return !ret;
}

JNIEXPORT jboolean JNICALL
Java_com_sswl_BsDiff_diff(JNIEnv *env, jclass type, jstring oldFilePath_, jstring newFilePath_,
                             jstring patchPath_) {
    const char *ch[5] = {0};
    ch[0] = "bsdiff";
    ch[1] = (*env)->GetStringUTFChars(env, oldFilePath_, 0);
    ch[2] = (*env)->GetStringUTFChars(env, newFilePath_, 0);
    ch[3] = (*env)->GetStringUTFChars(env, patchPath_, 0);

    int ret = main_exec(bsdiff_main, 4, ch);

    (*env)->ReleaseStringUTFChars(env, oldFilePath_, ch[1]);
    (*env)->ReleaseStringUTFChars(env, newFilePath_, ch[2]);
    (*env)->ReleaseStringUTFChars(env, patchPath_, ch[3]);

    return !ret;
}

20.重新生成解决方案,编译成功
在这里插入图片描述
【备注】细心的小伙伴可能发现在上述日志中,其实还有个小警告

Previous IPDB not found, fall back to full compilation.
1>All 1 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.

解决方案是:选中项目,依次点击项目- 属性 - 链接器 - 优化 - 链接时间代码生成,选择使用链接时间代码生成(/LTCG),重新编译就没有警告了
21.咱们打开框起来的目录看看bspatch.dll,同时还有一个依赖到的bz2.dll
在这里插入图片描述

二、调用bspatch.dll

1、先在jni接口对应的com.sswl包名下,创建BsDiff.java,加载生成的.dll,注意一定要跟上面的jni接口一一对应

package com.sswl;

public class BsDiff {
    static {
        //这个bz2.dll是bspatch.dll所依赖到的,必须要写上来,而且必须在load bspatch.dll之前,否则会报错:
       //Exception in thread "main" java.lang.UnsatisfiedLinkError: ..\bspatch.dll: Can't find dependent libraries
        System.load("E:\\AS-workspace\\bspatch\\x64\\Release\\bz2.dll");
        System.load("E:\\AS-workspace\\bspatch\\x64\\Release\\bspatch.dll");
    }

    public static native boolean make(String oldFilePath, String newFilePath, String patchPath);

    public static native boolean diff(String oldFilePath, String newFilePath, String patchPath);
}

2、写一个类去调用一下,看看是否正常生成差分包

package com.sswl;

/**
 * @Description: 打差分包
 * @Author: jimmyliang
 * @CreateDate: 2020/9/4
 */
public class Main {

    public static void main(String[] args) {
        BsDiff.diff("C:\\Users\\Administrator\\Desktop\\old.apk",
                "C:\\Users\\Administrator\\Desktop\\new.apk",
                "C:\\Users\\Administrator\\Desktop\\patch.apk");

//        BsDiff.make("C:\\Users\\Administrator\\Desktop\\old.apk",
//                "C:\\Users\\Administrator\\Desktop\\new12.apk",
//                "C:\\Users\\Administrator\\Desktop\\patch.apk");
    }
}

3、运行一下上述main函数,可以顺利得到一个patch.apk
在这里插入图片描述
4.重新用生成的patch.apk合成之前new.apk,看看是否一致
在这里插入图片描述
对比一下MD5, 确实完全一致,那没问题了,到此结束啦,喜欢的去github自取吧~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值