打开创建的本地头文件,可以看见要实现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;
#endif
jobject:
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对象时可以调用指定的构造方法。
sign签名,用来表示要取得的属性或者方法的类型:
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 就可以了。