首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

c++ 经过JNI技术调用java代码使用总结

2012-11-09 
c++ 通过JNI技术调用java代码使用总结?由于工作需要,要写一段c++代码来调用java的api。下面把实现和调研的

c++ 通过JNI技术调用java代码使用总结

?

由于工作需要,要写一段c++代码来调用java的api。下面把实现和调研的过程总结出来。

?

1. 如何解决?

首选JNI,首先对JNI的原理和使用方法简单调研一下,JNI的权威资料是:

http://java.sun.com/docs/books/jni/html/jniTOC.html

?

简单点说,JNI可以帮助我们解决两个问题:

1)实现java代码调用其他代码(c,c++,...)

大致的做法:

a)写java 类

?

 class HelloWorld {     private native void print();     public static void main(String[] args) {         new HelloWorld().print();     }     static {         System.loadLibrary("HelloWorld");     } }

?

b)把需要由其他语言实现的逻辑在java类中用native关键字标示(注意native 方法只能有声明,不能有实现)

?

private native void print();

?

c)javac编译java类,获得对应的class文件

javac HelloWorld.java

?

d)javah -jni 生成本地方法对应的头文件

javah -jni HelloWorld

?

查看生成的头文件,核心部分是:

?

 JNIEXPORT void JNICALL  Java_HelloWorld_print (JNIEnv *, jobject);

?

e)编写本地方法的代码(c或者c++代码)

?

 #include <jni.h> #include <stdio.h> #include "HelloWorld.h"  JNIEXPORT void JNICALL  Java_HelloWorld_print(JNIEnv *env, jobject obj) {     printf("Hello World!\n");     return; }

?

?

f)编译本地方法,生成可执行文件

?

cc -G -I/java/include -I/java/include/solaris      HelloWorld.c -o libHelloWorld.so

?

cl -Ic:\java\include -Ic:\java\include\win32      -MD -LD HelloWorld.c -FeHelloWorld.dll

?

?

g)运行java代码,你会发现c写的本地方式实现被成功调用。

?

2)实现其他代码(比如c++)调用java代码(这正是我要解决的问题)

?

jni是双向的,java可以调用其他语言实现的native方法,同样其他语言同样可以调用java代码。如何做呢?由于java代码必须(一定)要运行在JVM中,因此其他语言要调用java代码,首先必须启动一个JVM实例,然后再通过JNI规范中给出的一些方法(具体参考上面给出的url),来实现对java代码的调用。

?

具体做法,以我实际解决的问题为例吧:

a)我要调用的java API是:

?

?

jstring jip = env->NewStringUTF(ip.c_str());jstring jpath = env->NewStringUTF(path.c_str());jint jlogLength = logLength;jbyteArray obj = (jbyteArray)env->CallStaticObjectMethod(clazz,mid,jip,jpath,jlogLength);

?

这里要注意的是,首先要把c++的类型转换为jni的类型。

?

另外,我的java方法返回的是byte[],而jni并没有对应的CallStaticByteArrayMethod,找了半天发现CallStaticObjectMethod可用。

?

jni的方法命名很有规律,Call<Static><ReturnType>Method.

?

CallStaticObjectMethod方法返回的是jobject类型,而

jbyteArray 是其子类,所以这里强转。接下来就是如何把jni的jbyteArray转换为c++的vector<char>了。

char* data = (char*)env->GetByteArrayElements(obj, 0);

size_t len = strlen(data);

for(size_t i = 0; i<len; i++)

{

container.push_back(data[i]);

}

?

另外还需要注意:

JNI规范指出,一个进程内只能开启一个JVM实例,否则就会报错。因此你必须确保JavaVM* jvm 在进程内绝对唯一。做法有singleton,static等。我采取static做法,但是我在实际测试中却发现在一个进程内重复调用两次ReadLog方法(具体做法是在UT只中运行两个case,每个case都会调用ReadLog方法,而UT的两个case是在一个进程内执行的)时,后面的一次会失败,而且非常严重,直接这样了:“A fatal error has been detected by the Java Runtime Environment”。为啥呢?我个人的分析(可能不对):我们知道一个JVM实例是在他运行的main方法退出时,自动也就结束了。第一次调用ReadLog方法结束后,当前进程启动的JVM应该也结束了,因为它已经没啥事可干了。那紧接着第二次调用ReadLog时,BeginJVM方法判断jvm这个指针 != NULL,于是它不再创建JVM了,但是这个jvm实例实际已经不可用了。以上是我的分析,有点武断,欢迎大家讨论,解惑。

?

以上的分析是完全错误的!

jvm何时退出?jni启动的jvm实例是作为子进程存在的,只要主进程仍然存在,那么jvm子进程就存在,不会退出,因此我们才不需要多次创建jvm实例。

小结

回过头看,使用jni也没啥难的,就是有些繁琐,需要好好看看jni的spec。以上是我的第一次jni之旅,希望对大家有帮助。


?

热点排行