您的位置:首页 > 编程语言 > C语言/C++

JNI转换通俗易懂的总结(C++调用java篇)

2017-06-28 15:32 239 查看
主要的内容分成以下5部分:

1. 启动java虚拟机(JVM),C++和java接口的交互,实际上就是C++和JVM的交互。

例子如下:

JavaVMOption options[3];

JNIEnv *env;

JavaVM *jvm;

JavaVMInitArgs vm_args;

long status;

char normalOpt[] ="-Djava.class.path=.:/home/itzxyy/zrf/jar/KafkaAdmin.jar";

char extOpt1[] = "-Xms250m";

char extOpt2[] = "-Xmx250m";

options[0].optionString = normalOpt;

options[1].optionString = extOpt1;

options[2].optionString = extOpt2;

vm_args.version = JNI_VERSION_1_6;

vm_args.nOptions = 3;

vm_args.options = options;

vm_args.ignoreUnrecognized = JNI_TRUE;

status = JNI_CreateJavaVM(&jvm,(void**) &env, &vm_args);

--以上代码貌似很复杂,其实就做了一个动作,效果如:java –jar -Xms250m -Xmx250m KafkaAdmin.jar ,是的,这么多杂七杂八的代码作用就是启动一个虚拟机,并且把虚拟机的指针赋予JavaVM *jvm和JNIEnv *env这两个指针成员。前者是虚拟机的指针,多线程共享一个jvm,后者是JNI的一个函数表指针,该函数表存储着我们编写JNI所需要的几乎所有函数接口。不同线程的env是独立的。

2. JNI类型的定义:

例子如下:

public:

jobject singleObj;

jclass singleObjClass;

jclass strClass;

jmethodID strctorID;

jstring encoding;

jmethodID mid_createProducer;

jmethodID mid_send;

jmethodID mid_flush;

jmethodID mid_close;

--对于JNI而言,它更像一门脚本语言,有自己规范的类型定义

Jobject对应java的Object类

jclass对应java的Class类

jmethodID对应java的方法指针

jstri ng对应java的String类

Jint、jlong、jdouble等类型分别对应java的相关基础类型

3. 获取要调用的java的方法id和成员id和类对象等

例子如下:

获取类对象:

strClass =env->FindClass("Ljava/lang/String;");

singleObjClass = env->FindClass("test/KafkaAdmin");

--获取类对象在后续获取方法指针和指向成员指针时需要作为传入参数,等价于java的String.getClass()和KafkaAdmin.getClass()

获取方法指针:

jmethodID ctorID =env->GetMethodID(singleObjClass, "<init>","()V");

strctorID = env->GetMethodID(strClass,"<init>","([BLjava/lang/String;)V");

--获取构造方法指针,后续实例化对象需要作为传入参数

实例化类对象:

singleObj = (jobject)(env)->NewObject(singleObjClass, ctorID);

(jstring) (env)->NewObject(strClass,strctorID, jbytes, encoding);

--调用构造方法实例化类对象,需要传入类对象,构造方法指针,构造方法中所需参数

获取方法id:

env->GetMethodID(singleObjClass,methodName, sig);

--需要传入类对象,方法名,类型签名

获取成员的标识id:

env->GetFieldID(myCls, “mNumber”, “I”);

--需要传入类对象,成员名,类型签名

*****根据上面例子可以看出JNIEnv *env的重要性,它包含了常用的JNI方法,比如FindClass,NewObject,GetMethodID,GetFieldID,顺便说一下获取静态方法id和静态成员id的方法名为:GetStaticMethodID,GetStaticFieldID,参数跟非静态一样

*****类型签名解释:

基础类型的签名:





Java类类型的签名:

Java类类型以L开头,以“/”分隔包名,在类名后加上“;”分隔符,例如String的签名为:Ljava/lang/String;

方法的签名格式:

(参数1类型签名参数2类型签名参数3类型签名.......)返回值类型签名

4. 操作java的属性和方法

env->CallVoidMethod(singleObj,mid_deleteTopic, zk, topic);

--需要传入实例对象,方法id,方法的传入参数

jintmNum = env->GetIntField(obj, mNumFieldID);

env->SetIntField(obj,mNumFieldID, mNum+100);

--获取和设置对象成员,需要传入实例对象,成员id

***** Call+返回类型+Method是操作方法的规范格式,同理,对成员的操作也是Get(Set)+成员类型+Field

5. 手工管理JVM内存

由于JNI是通过JNIEnv *env和JVM进行交互的,这样就产生一个问题,JNI不可避免地会持有对JVM中某些对象的引用,但JVM不能通过常规的gc机制去感知JVM中对象在JNI中的引用的生命周期,所以使用JNI就必须手动去管理引用,去通知JVM引用的生命周期已经结束,以便JVM中的GC线程能正常回收堆内存,否则会造成内存泄露的情况。

例如:

(env)->ReleaseStringUTFChars(str,chars);

Release<PrimitiveType>ArrayElements env:JNI 接口指针。 array:Java 数组对象。elems:指向数组元素的指针。mode:释放模式。

env->DeleteLocalRef(js);//删除局部引用

DeleteGlobalRef(jobject gref);//删除全局引用

---在JNI中对JVM中对象的引用分为局部引用,全局引用,弱全局引用(这个少用),最常见的是局部引用,局部引用是线程私有的,全局引用是线程共享的,对于全局引用而言必须显著手工删除,对于局部引用而言,如果是java调用native代码则可以认为引用只在调用过程中存活,在调用接收后引用会自动被删除,但是如果是大对象的引用或者是分配引用太多的话最好也手工删除。如果是C++调用java的话,那么引用可能会在C++程序的生命周期中一直存活着,JVM无法感知引用是否销毁,所以必须得显著删除,否则JVM的GC机制无法回收引用对应的对象,从而造成内存泄露。

总结:

JNI其实某种程度来看更像是门类似C++的脚本语言,有自己的类型定义:jint,jlong,jstring等,有自己的函数,通过JNIEnv *env这个函数表指针可以调用所需要的函数,通过获取方法id,成员id,等信息操控JVM中的方法和对象。最后还要注意内存的管理,及时通知JVM删掉无用的引用而方便GC。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: