打开创建的本地头文件,可以看见要实现c++的方法:
/* * Class: com_cn_TestJni * Method: getjni * Signature: ()V */ JNIEXPORT void JNICALL Java_com_cn_TestJni_getjni (JNIEnv *, jobject); /* * Class: com_cn_TestJni * Method: getj * Signature: ()V */ JNIEXPORT void JNICALL Java_com_cn_TestJni_getj (JNIEnv *, jclass);下面来介绍下方法中JNIEnv ,jobject ,jclass代表的含义。
JNIEnv:
JNIEnv类型代表了Java环境。通过这个JNIEnv*z指针,可以对Java端的代码进行操作。例如创建Java类的对象,调用Java对象的方法,获取Java对象的属性等。JNIEnv的指针会被JNI传入到本地方法的实现函数中来对Java端的代码进行操作。
/* * JNI Native Method Interface. */ struct JNINativeInterface_; struct JNIEnv_; #ifdef __cplusplus typedef JNIEnv_ JNIEnv; #else typedef const struct JNINativeInterface_ *JNIEnv; #endifjobject:
class _jobject {}; typedef _jobject *jobject;本地方法是非静态方法,传递进来的jobject是指向java对象的引用,与调用该本地方法相关联的对象的引用。
jclass:
class _jobject {}; class _jclass : public _jobject {}; typedef _jclass *jclass;
本地方法是静态方法,传递进来的jclass代表了调用该静态方法类的class对象,即该Class类字节码。
jclass的取得:
为了能够在c/c++中使用Java类。JNI.h头文件中专门定义了jclass类型来表示Java中的Class类。
JNIEnv类中有如下几个简单的函数可以取得jclass:
jclass DefineClass(const char *name, jobject loader, const jbyte *buf, jsize len) { return functions->DefineClass(this, name, loader, buf, len); } jclass FindClass(const char *name) { return functions->FindClass(this, name); } jclass GetSuperclass(jclass sub) { return functions->GetSuperclass(this, sub); } jclass GetObjectClass(jobject obj) { return functions->GetObjectClass(this,obj); }FindClass会在classpath系统环境变量下寻找类,传入完整类名,注意包与包之间用“/”分隔,如:
jclass cls_string=env->FindClass("java/lang/String");
java的数据类型跟c/c++的数据类型映射关系:
访问Java类中的属性与方法:
在本地代码中访问Java端的代码,一个常见的应用就是获取类的属性和调用类的方法,为了在c/c++中表示属性和方法,JNI在jni.h头文件中定义了jfieldID,jmethodID类型来分别代表Java端的属性和方法。
我们在访问或是设置Java属性的时候,首先就要在本地代码中取得代表该Java属性的jfieldID,然后才能在本地代码进行Java属性操作。同样的,我们需要呼叫Java端方法时,也是需要取得代表该方法的jmethodID才能进行java方法调用。
JNIEnv获取相应的jfieldID和jmethodID,可以看到它们的参数列表是相同的,是要获取java对象的属性/方法的该对象的Class ,属性或者方法名 ,属性或者方法类型:
jfieldID GetFieldID(jclass clazz, const char *name, const char *sig) { return functions->GetFieldID(this,clazz,name,sig); } jmethodID GetMethodID(jclass clazz, const char *name, const char *sig) { return functions->GetMethodID(this,clazz,name,sig); } jfieldID GetStaticFieldID(jclass clazz, const char *name, const char *sig) { return functions->GetStaticFieldID(this,clazz,name,sig); } jmethodID GetStaticMethodID(jclass clazz, const char *name, const char *sig) { return functions->GetStaticMethodID(this,clazz,name,sig); }GetMethodID也能够取得构造函数的jmethodID,创建一个java对象时可以调用指定的构造方法。
javap:
JDK提供的工具来查看一个类的声明。其中就可以设置输出每个属性/方法的签名。
效果:
public class TestJni { private String s; public TestJni() { super(); }; public String getS() { System.out.println("getS:" + s); return s; } public void setS(String s) { this.s = s; System.out.println("setS:" + s); } public native void getjni(); public static native void getj(); }
获取/设置java属性:
取得属性相对应的jfieldID后,就可以使用set<Type>Field,get<Type>Field,setStatic<Type>Field和GetStatic<Type>Field等来对Java属性进行操作。
如取得/设置Int类型属性(其他类型类似):
jint GetIntField(jobject obj, jfieldID fieldID) { return functions->GetIntField(this,obj,fieldID); } void SetIntField(jobject obj, jfieldID fieldID, jint val) { functions->SetIntField(this,obj,fieldID,val); } jint GetStaticIntField(jclass clazz, jfieldID fieldID) { return functions->GetStaticIntField(this,clazz,fieldID); } void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value) { functions->SetStaticIntField(this,clazz,fieldID,value); }java方法的调用:
JNIEnv提供了Call<Type>Method ,CallStatic<Type>Method和CallNonVirtual<Type>Method,通过传入的jmethodID来来调用方法。
调用实例方法的三种形式:
jint CallIntMethod(jobject obj, jmethodID methodID, ...) { va_list args; jint result; va_start(args,methodID); result = functions->CallIntMethodV(this,obj,methodID,args); va_end(args); return result; } jint CallIntMethodV(jobject obj, jmethodID methodID, va_list args) { return functions->CallIntMethodV(this,obj,methodID,args); } jint CallIntMethodA(jobject obj, jmethodID methodID, const jvalue * args) { return functions->CallIntMethodA(this,obj,methodID,args); }第一种是最常用的方法。
第二种是当调用这个函数的时候有一个指向参数表的va_list变量时使用
第三种是当调用这个函数的时候有一个指向jvalue或jvalue数组的指针时用
调用静态方法的三种形式:
jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, ...) { va_list args; jobject result; va_start(args,methodID); result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); va_end(args); return result; } jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, va_list args) { return functions->CallStaticObjectMethodV(this,clazz,methodID,args); } jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, const jvalue *args) { return functions->CallStaticObjectMethodA(this,clazz,methodID,args); }实例:
在java端声明属性和方法:
public class TestMain { public static int i = 10; public void tPrint(int i) { System.out.println("call tPrint:" + i); } public native void getjni(); public static void main(String[] args) { // TODO Auto-generated method stub System.loadLibrary("ConsoleApplication"); TestMain j = new TestMain(); j.getjni(); System.out.println(j.i); } }在c++中调用属性和方法;
JNIEXPORT void JNICALL Java_com_cn_TestMain_getjni (JNIEnv * env, jobject obj){ //获取class jclass cls=env->GetObjectClass(obj); /** 属性*/ //获取jfieldID jfieldID iid=env->GetStaticFieldID(cls,"i","I"); //获取属性 jint iint=env->GetStaticIntField(cls,iid); cout<<iint<<endl; //设置属性 env->SetStaticIntField(cls,iid,20L+iint); /** 方法*/ //获取jmethodID jmethodID mid=env->GetMethodID(cls,"tPrint","(I)V"); //调用方法 env->CallVoidMethod(obj,mid,15L); };打印结果;
callNonvirtual<Type>Method:
在JNI中顶以的CallNonVirtual<Type>Method能够实现子类调用父类方法的功能。如果想要调用一个对象的父类的方法,而不是子类的这个方法的话,就可以使用。
使用前,先要取得父类及其要调用的父类方法的jmethodID,传入到这个方法中就能通过子类对象调用被覆盖的父类方法了。
实例:
java端定义父类和子类;
public class TestFather { public void function() { System.out.println("call father: function"); } }
public class TestChild extends TestFather { public void function() { // TODO Auto-generated method stub System.out.println("call child: function"); } }在入口处声明:
public class TestMain { public native void getjni(); TestFather f = new TestChild(); public static void main(String[] args) { // TODO Auto-generated method stub System.loadLibrary("ConsoleApplication"); TestMain j = new TestMain(); j.getjni(); } }在c++中:
JNIEXPORT void JNICALL Java_com_cn_TestMain_getjni (JNIEnv * env, jobject obj){ jclass cls=env->GetObjectClass(obj); jfieldID fid=env->GetFieldID(cls,"f","Lcom/cn/TestFather;"); jobject fobj=env->GetObjectField(obj,fid); jclass cls_f=env->FindClass("com/cn/TestFather"); jmethodID methodid_f=env->GetMethodID(cls_f,"function","()V"); env->CallVoidMethod(fobj,methodid_f); env->CallNonvirtualVoidMethod(fobj,cls_f,methodid_f); };结果:
注意:
1.javac 找不到符号
把关联的类都编译下:javac *.java 就可以了。