一. 准备环境
#检查jdk版本
rpm -qa |grep java
#如果没有openjdk-devel包,执行:
yum install java-1.7.0-openjdk-devel -y
#查找JAVA_HOME
rpm -ql java-1.7.0-openjdk-devel-1.7.0.241-2.6.20.0.el7_7.x86_64
#将JAVA_HOME加入环境变量
echo "export JAVA_HOME='/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.241-2.6.20.0.el7_7.x86_64'" >> /etc/profile
source /etc/profile
并在/etc/ld.so.conf.d目录下,创建一个jni.conf,向其中写入:
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.241-2.6.20.0.el7_7.x86_64/jre/lib/amd64/server
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.241-2.6.20.0.el7_7.x86_64/jre/lib/amd64/
执行ldconfig -v |grep libjava,如果有结果显示,就说明配置正确。
如果没配置好,可能会出现如下问题:
Error occurred during initialization of VM
java/lang/NoClassDefFoundError: java/lang/Object
二. 编写代码如下:
#include <jni.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/*C字符串转JNI字符串*/
jstring stoJstring(JNIEnv* env, const char* pat) {
jclass strClass = (*env)->FindClass(env, "Ljava/lang/String;");
jmethodID ctorID = (*env)->GetMethodID(env, strClass, "<init>",
"([BLjava/lang/String;)V");
jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = (*env)->NewStringUTF(env, "utf-8");
return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes,
encoding);
}
/*JNI字符串转C字符串*/
char* jstringTostring(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env, "java/lang/String");
jstring strencode = (*env)->NewStringUTF(env, "utf-8");
jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
"(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
strencode);
jsize alen = (*env)->GetArrayLength(env, barr);
jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
if (alen> 0) {
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env, barr, ba, 0);
return rtn;
}
int test(char* strPath) {
int res = 0;
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
/*设置初始化参数*/
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.:${JAVA_HOME}:/tmp/test-1.0.jar"; //这里指定了要使用的第三方Jar包
options[2].optionString = "-verbose:NONE"; //用于跟踪运行时的信息
/*版本号设置不能漏*/
vm_args.version=JNI_VERSION_1_4;//jdk版本目前有1.1,1.2,1.4 只要比你的jdk的版本低就可以 我用的是openjdk1.7的版本
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res < 0 || jvm == NULL || env == NULL) {
fprintf(stderr, "Can't create Java VM\n");
return -1;
}
fprintf(stdout, "ok 调用JNI_CreateJavaVM创建虚拟机\n");
/*获取实例的类定义*/
jclass cls = (*env)->FindClass(env, "test/myTest"); //这里是jar包内myTest类的具体路径
if (cls == 0) {
fprintf(stderr, "FindClass failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return -1;
}
fprintf(stdout, "ok 返回JAVA类的CLASS对象\n");
/*创建对象实例*/
jobject obj = (*env)->AllocObject(env, cls);
if (obj == NULL) {
fprintf(stderr, "AllocObject failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return -1;
}
fprintf(stdout, "ok 获取该类的实例\n");
/*获取构造函数,用于创建对象*/
/***1.1可用""作为构造函数, 1.2用"<init>"参数中不能有空格
"(Ljava/lang/String;)V"*/
jmethodID mid = (*env)->GetMethodID(env, cls, "setParam",
"(Ljava/lang/String;)V"); //(Ljava/lang/String;)代表传入参数为String类型,V代表返回值为void
if (mid == 0) {
fprintf(stderr, "GetMethodID failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return -1;
}
fprintf(stdout, "ok 获取类中的方法\n");
jstring path = stoJstring(env, strPath); //将char*转换为java对应的数据类型
//jlong kpje = (jlong)atoi(argv[4]);
(*env)->CallObjectMethod(env, obj, mid, path);
//(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)代表三个入参数,且都为String类型,Z代表返回值为boolean类型
mid = (*env)->GetMethodID(env, cls, "test",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
if (mid == 0) {
fprintf(stderr, "GetMethodID failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return -1;
}
fprintf(stdout, "ok 获取类中的方法\n");
//调用,并判断返回值
jboolean ret = (jboolean) (*env)->CallObjectMethod(env, obj, mid, "test1", "test2", test3);
if (ret == 0) {
fprintf(stderr, "failed!!!\n");
res = -1;
}else{
fprintf(stderr, "OK!!!\n");
res = 0;
}
/*捕捉异常*/
if ((*env)->ExceptionOccurred(env)) {
/* 如果有异常,打印调用栈信息 */
(*env)->ExceptionDescribe(env);
return -1;
}
/*销毁JAVA虚拟机*/
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return res;
}
int main(int argc, char **argv) {
int ret = test("mypath");
fprintf(stderr, "RET: %d\n", ret);
}
编译指令为:
gcc -o jni jni.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -L${JAVA_HOME}/jre/lib/amd64/server -ljvm
编译成功后,生成jni可执行文件。
三、数据类型的转换
主要数据类型的对应关系如下所示:
|java类型 | jni本地类型 | c语言
|int | jint | int(32bit)
|boolean | jboolean | unsigned char(8bit)
|float | jfloat | float(32bit)
|char | jchar | unsigned short(16bit)
|byte | jbyte | char(8bit)
|String | jString | 无
|int[] | jintArray | int[]
其中,java的String类型与c/c++的char*互相转换的代码入下所示:
/*C字符串转JNI字符串*/
jstring stoJstring(JNIEnv* env, const char* pat) {
jclass strClass = (*env)->FindClass(env, "Ljava/lang/String;");
jmethodID ctorID = (*env)->GetMethodID(env, strClass, "<init>",
"([BLjava/lang/String;)V");
jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = (*env)->NewStringUTF(env, "utf-8");
return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes,
encoding);
}
/*JNI字符串转C字符串*/
char* jstringTostring(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env, "java/lang/String");
jstring strencode = (*env)->NewStringUTF(env, "utf-8");
jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
"(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
strencode);
jsize alen = (*env)->GetArrayLength(env, barr);
jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
if (alen> 0) {
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env, barr, ba, 0);
return rtn;
}
四、GetMethodID函数
jni通过GetMethod函数获取java类中的成员函数,是以"(*)+"形式表示函数的有哪些传入参数,传入参数的类型,返回值的类型。"()" 中的字符表示传入参数,后面的则代表返回值。
例如:
"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
"(Ljava/lang/String;Ljava/lang/String;)I".表示 int Func(String,String)
"I"表示该变量是Int类型,"Ljava/lang/String;"表示该变量是String类型,
其返回值类型的对应关系如下:
|Java 类型|符号|
| Boolean |Z |
| Byte |B |
| Char |C |
| Short |S |
| Int |I |
| Long |L |
| Float |F |
| Double |D |
| Void |V |
参考:
来源:CSDN
作者:vah101
链接:https://blog.csdn.net/vah101/article/details/103945916