一叶梧桐 2014-11-14
在做一个医疗设备的项目的时候由于涉及到心电图数据的解析,因为解析只能依靠c,所以需要java调用c生成的动态库去解析数据。
因为用JNI调用Linux下的动态库,要求java类名和动态库中的名保持一致,所以只能通过生成一个libFileUtil.so去调用客户提供的libauto_analyse.so。
1. 确保gcc编译器已安装
2. 编写FileUtil.java 代码,用native声明需要用c实现的函数。
如果源程序是包含在package的话,应该建立同样的文件夹结构,如com/hnrbyl/rmas/util/FileUtil.java
package com.hnrbyl.rmas.util;
public class FileUtil {
public native static String ResultText();
public static void main(String[] args) {
System.loadLibrary("FileUtil");
String s=FileUtil.ResultText();
System.out.println(s);
}
}
项目中的代码:
public class FileUtil {
//要调用.so中的方法
public native static String ResultText(String txtPath, String pdfPath);
public static String autoAnalyse(String txtPath, String pdfPath){
//加载自动分析已上传心电图的动态链接库文件
System.loadLibrary("FileUtil");
String jsonData = FileUtil.ResultText(txtPath, pdfPath);
return jsonData;
}
}
3. 在FileUtil.java文件所在目录下编译.java文件
Javac FileUtil.java
4. 编译第三步生成的.class文件,生成对应的.h头文件,本例中生成com_hnrbyl_rmas_util_FileUtil
Javah –classpath ~workspace\elect\WebRoot\WEB-INF\classes -d d:\ -jni com.hnrbyl.rmas.util.FileUtil
其中java中各个命令的意思
-classpath <路径> 用于转入类的路径
-d <目录> 输出目录
-jni 生成JNI样式的头文件(默认)
这个Java文件是在路径“E:\workspace\elect\src”下,包“package com.hnrbyl.rmas.util;”中的
得到的FileUtil 对应的class文件,在路径“E:\workspace\elect\WebRoot\WEB-INF\classes\com\hnrbyl\rmas\util”下。
注意到以上我们命令中指定的路径
注意到我们的命令符的执行位置是源代码目录” E:\workspace\elect\src\”
-classpath 后面的路径是指包” com.hnrbyl.rmas.util”所在的根路径(如下图所示)
5. 第四部中生成的头文件内容
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class FileUtil */
#ifndef _Included_com_hnrbyl_rmas_util_FileUtil
#define _Included_com_hnrbyl_rmas_util_FileUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: FileUtil
* Method: ResultText
* Signature: (I)I
*/
JNIEXPORT jstring JNICALL Java_com_hnrbyl_rmas_util_FileUtil_ResultText(JNIEnv*,
jobject);
#ifdef __cplusplus
}
#endif
#endif
项目中的代码:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class FileUtil */
#ifndef _Included_com_hnrbyl_rmas_util_FileUtil
#define _Included_com_hnrbyl_rmas_util_FileUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_hnrbyl_rmas_util_FileUtil
* Method: ResultText
* Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_hnrbyl_rmas_util_FileUtil_ResultText
(JNIEnv *, jclass, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
6. 编写FileUtil.c文件,实现引用第4步中生成的.h头文件,并声明其中的方法。
#include "jni.h"
#include "stdlib.h"
#include "FileUtil.h"
//
JNIEXPORT jstring JNICALL Java_com_hnrbyl_rmas_util_FileUtil_ResultText(JNIEnv *env,
jobject obj)
{
char* str = "Hello World!";
jstring rtn;
rtn = env->NewStringUTF(str);
return rtn;
}
项目中的代码:
#include "stdlib.h"
#include "stdio.h"
#include "FileUtil.h"
#include "dlfcn.h"
#include "jni.h"
JNIEXPORT jstring JNICALL Java_com_hnrbyl_rmas_util_FileUtil_ResultText
(JNIEnv *env, jobject obj, jstring txtPath, jstring pdfPath)
{
char** (*myso)(char* a, char* b);//function pointer
void *handle;
char *errorInfo;
handle=dlopen("libauto_analyse.so",RTLD_LAZY);//open lib file
errorInfo = dlerror();
// 如果返回 NULL 句柄,表示无法找到对象文件,过程结束。否则的话,将会得到对象的一个句柄,可以进一步询问对象
if (errorInfo){
// 如果返回 NULL 句柄,通过dlerror方法可以取得无法访问对象的原因
printf("Open Error:%s.\n",dlerror());
return 0;
}
// 使用 dlsym 函数,尝试解析新打开的对象文件中的符号。您将会得到一个有效的指向该符号的指针,或者是得到一个 NULL 并返回一个错误
myso=dlsym(handle,"auto_analyse");//call dlsym function
errorInfo = dlerror();// 调用dlerror方法,返回错误信息的同时,内存中的错误信息被清空
if (errorInfo){
printf("Dlsym Error:%s.\n",errorInfo);
return 1;
}
char* a=(*env)->GetStringUTFChars(env, txtPath, 0);
char* b=(*env)->GetStringUTFChars(env, pdfPath, 0);
char **str=(*myso)(a,b);
dlclose(handle);
(*env)->ReleaseStringUTFChars(env, txtPath, a);
(*env)->ReleaseStringUTFChars(env, pdfPath, b);
jstring rtn;
rtn = (*env)->NewStringUTF(env,str[1]);
return rtn;
}
7. 讲第6步中编写的FileUtil.c文件,编译成.so文件
gcc -I/wenjin/jdk1.7.0_25/include/ -I/wenjin/jdk1.7.0_25/include/linux/ -fPIC -shared -o libFileUtil.so FileUtil.c
注:/ wenjin/jdk1.7.0_25/include 是jni.h头文件所在的路径
/ wenjin/jdk1.7.0_25/include/linux 是jni_md.h所在的路径
8. 将第7步中生成的libFileUtil.so文件拷贝到java的加载库LD_LIBRARY_PATH指向的路径中。
9. 在运行FileUtil.class
在com.hnrbyl.rmas.util跟目录src下运行
Java com.hnrbyl.rmas.util.FileUtil
一切正常后,可以看到运行結果是输出了Hello World!,其值是由C语言代码实现并返回的。
gcc -I/usr/java/jdk1.7.0_25/include/ -I/usr/java/jdk1.7.0_25/include/linux/ -lauto_analyse -lhpdf -lz -lm -ljson-c -fPIC -shared -o libFileUtil.so FileUtil.c
执行该句的问题。
问题:
/usr/bin/ld: /usr/local/lib/libz.a(crc32.o): relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
/usr/local/lib/libz.a: could not read symbols: Bad value
一般是64 位 电脑才会出现。
解决方法如下:
cd zlib-1.2.3 //进入zlib目录
CFLAGS="-O3 -fPIC" ./configure //使用64位元的方法进行编译
make
make install
make clean
4externintadd(inta,intb);8inti=0;9printf("helloworld!\n");10printf("label_abc=%d\n",&&label_abc);11/