java调用第三方64位so库

第三方给了so文件和一个.c和.h调用例子,还有一个.a的库

但是通过nm查看so的方法,发现想要调用的方法返回值是void ,返回值是定义在入参中的(不懂c语言),于是只能通过生成一个中间库的方式去调用第三方库,然后通过中间库暴露有返回值的方法供jna调用

通过nm命令查看so的方法

nm XXX.so

其中为 U 的方法是未被定义的,不可以直接调用

1. 编译中间库

给的例子中调用了一个.a库中的方法(我没找到引入的地方),头文件简单如下

#ifdef __cplusplus
extern "C"{
#endif
void Encode(unsigned char *passwd,unsigned char *result);
#ifdef __cplusplus
  }
#endif

.c例子源代码中只有一个man方法调用.a中的方法打印一下

int main()
{ 
  unsigned char str2[12];
  Encode("990818",str2);
  printf("%s\n",str2);
}

在这个文件中新建一个方法

char* getEncode(char* a)
{ 
unsigned char str2[12];
printf("before encode %s\n",a);
 Encode(a,str2);
 //char temp[256] = "456"; 
//memcpy(a, temp, strlen(temp)) ;
printf("after encode %s\n",str2);
char *s = malloc(100);
    strcpy(s, str2);
//char* aa=str2;
//printf("%s\n",aa);
//jstring encodePas=(*env)->NewStringUTF(aa);
  return  s;
}

注意下返回值是不是你java代码中需要的

有一点,我不是通过jni的那种先根据class生成.h文件编辑的,所以不需要在c语言中引入jni之类的模块,所以我这样的方法也不会在java中调用时抛出jvm相关的错误

2. 将中间库编译成新的so文件

在linux中安装gcc后编译

gcc demo.c -fPIC -I/usr/local/mysoft/jdk/jdk1.8.0_231/include -I/usr/local/mysoft/jdk/jdk1.8.0_231/include/linux -shared -o liencode.so libgtjautil64.a

有几点很重要

-I 是指定jdk对应的库,不指定的话可能会报错

因为我的这个项目引用了.a库文件,所以一定一定要在编译的时候把.a文件也编译上,不然运行会找不到方法

3.编写java代码(这应该是最简单的了)

String os = System.getProperty("os.name");  // 获取当前操作系统的类型
    int beginIndex = os != null && os.startsWith("Windows") ? 1 : 0;// windows操作系统为1 否则为0


    // [Native.synchronizedLibrary] 阻止多线程同时访问本地代码
    Clibrary INSTANTCE = (Clibrary) Native.synchronizedLibrary(
            (Clibrary) Native.loadLibrary(
                    EncodePasUtils.filePath
//                            .getPath()
                            .substring(beginIndex)
                    , Clibrary.class
            )
    );


    String getEncode(String password);

然后去调用就大功告成了(需要在linux环境下)

4.打包发布

因为这个项目我是准备打成一个jar供其它项目调用的,我又不想定义绝对路径在每个服务器都要放个so文件,所以需要通过流去将so文件读取生成到服务器的某个目录,然后再去调用它

static {

        logger.info("begin write so to temp ");
        String systemType = System.getProperty("os.name");
//        String libExtension = (systemType.toLowerCase().indexOf("win")!=-1) ? ".dll" : ".so";
        String libExtension = ".so";

        String libFullName = "liencode" + libExtension;
        //动态库的输出目录 可自行设置
        String nativeTempDir = System.getProperty("java.io.tmpdir");
        logger.info("write so path  "+nativeTempDir);
        InputStream in = null;
        BufferedInputStream reader = null;
        FileOutputStream writer = null;

        File extractedLibFile = new File(nativeTempDir+File.separator+libFullName);
        if(!extractedLibFile.exists()){
            try {
                // “/”代表Jar包的根目录
                in = Clibrary.class.getResourceAsStream("/" + libFullName);
                if(in==null){
                    in =  Clibrary.class.getResourceAsStream(libFullName);
                }
                Clibrary.class.getResource(libFullName);
                reader = new BufferedInputStream(in);
                writer = new FileOutputStream(extractedLibFile);

                byte[] buffer = new byte[1024];

                while (reader.read(buffer) > 0){
                    writer.write(buffer);
                    buffer = new byte[1024];
                }
            } catch (IOException e){
                logger.info("read so fail-----------------------");
                e.printStackTrace();
            } finally {
                if(in!=null){
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(writer!=null){
                    try {
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

然后打包的时候指定maven打包插件

 <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.2.0</version>

                <configuration>
                    <!--       必须要指定 1.7 不然项目不兼容             -->
<!--                    <source>1.7</source>-->
<!--                    <target>1.7</target>-->

                    <encoding>UTF-8</encoding>
                    <archive>
                        <manifest>
                            <!--                            <mainClass>com.gd.net.NetMain</mainClass>-->
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <!--下面是为了使用 mvn package命令,如果不加则使用mvn assembly-->
                <executions>
                    <execution>
                        <id>make-assemble</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

因为服务器代码使用jdk1.7编译,并且spring都是3.几的代码,所以不能用1.8去编译,不然运行报错

打包还要注意,因为我本地为了方便测试用的springboot并且打包是可执行的包,但是当作依赖的话springboot依赖的包太多了,把它去掉并打成依赖包即可

通过下面的命令将jar安装到本地maven库

mvn install:install-file -Dfile=XXXX.jar -DgroupId=com.linkstec -DartifactId=XXXX -Dversion=0.0.1-SNAPSHOT -Dpackaging=jar

大功告成!

猜你喜欢

转载自blog.csdn.net/wkfyynh/article/details/104863163