在第二讲中讲到了C++中操作java程序的方法以及操作步骤,下面就通过实例的方式来体会到底怎么通过JNI提供的方法来
获取、操作 java属性、方法以及父类方法
1、取得/设定Java属性值
在原来java 类TestNative的基础上编写如下代码:
package com.fomagic;
public class TestNative {
public native void sayHello(); //C++本地代码实现
public int number=10; //定义变量初始值为10,在C++中进行修改
public static void main(String[] args) {
System.loadLibrary("NativeCode");//加载动态链接库,不能加 .dll
TestNative test=new TestNative(); //创建本类实例
test.sayHello();
System.out.println("C++中操作之后的结果: "+test.number);
}
}
C++中原有代码添加如下:
JNIEXPORT void JNICALL Java_com_fomagic_TestNative_sayHello (JNIEnv *env, jobject obj)
{
cout<<"Hello World !"<<endl;
jclass clazz_TestNative=env->GetObjectClass(obj); //通过传入的obj类的对象获取当前类
jfieldID id_number=env->GetFieldID(clazz_TestNative,"number","I"); //类实例,属性名,(sign)数据类型
jint number =env->GetIntField(obj,id_number); // 通过 ID 获取当前变量
cout<<"原数据: "<<number <<endl;
env->SetIntField(obj,id_number,100L); //类实例,变量ID, 修改的java属性值
}
注:修改的属性值 100L,可以参看第二讲的 “Java类型在C/C++中的映射关系” 图表 (java中 int 对应 C++中 long)
2、调用Java类的方法
调用实例方法的三种形式;
Call<TYPE>Method(jobject obj ,jmethodID id ,….);
Call<TYPE>MethodV(jobject obj ,jmethodID id ,va_list lst);
Call<TYPE>Method(jobject obj ,jmethodID id ,jvalue* v) ;
第一个比较常用
第二个是当调用这个函数有一个指向参数表的va_list变量时使用(只知道是可变参数列表)
第三个当调用函数时有一个指向jvalue或jvalue数组的指针时使用(即传入的是所有参数的变量数组指针)
下面就以第一种方式写下返回较大值的实例:
package com.fomagic;
public class TestNative {
public native void sayHello(); //C++本地代码实现
public static void main(String[] args) {
System.loadLibrary("NativeCode");//加载动态链接库,不能加 .dll
TestNative test=new TestNative();
test.sayHello();
}
double max(double num1,double num2){
return num1>num2 ? num1 : num2;
}
}
JNIEXPORT void JNICALL Java_com_fomagic_TestNative_sayHello (JNIEnv *env, jobject obj)
{
jclass clazz_TestNative=env->GetObjectClass(obj); //获取当前类
jmethodID id_max=env->GetMethodID(clazz_TestNative,"max","(DD)D"); //获取方法ID(签名)
jdouble maxValue = env->CallDoubleMethod(obj,id_max,100.5,203.1); //调用max方法进行传值
cout<<"较大值: "<<maxValue <<endl; }
3、调用Java类父类方法
我们都知道在java中父类创建子类对象只能调用子类的覆盖方法,而C++中子类对象默认调用的父类的方法,当父类方法用虚拟函数关键字virtual修饰时,调用子类覆盖方法
java | C++ |
Father p=new Child();p.function(); //调用子类覆盖方法 | Father* p=new Child();p->function(); //默认调用父类方法,用virtual修饰符修饰则调用子类覆盖方法 |
通过JNI的 CallNonvirtual<TYPE>Method 可以实现子类对象调用父类被覆盖的方法的功能,步骤:
- 取得父类和需要调用的父类中方法的jmethodID
- 将jmethodID传入 CallNonvirtual<TYPE>Method
代码:新建Father类 、Child类
package com.fomagic;
public class TestNative {
public native void sayHello(); // C++本地代码实现
public Father p=new Child(); //创建Child 的实例对象
public static void main(String[] args) {
System.loadLibrary("NativeCode");// 加载动态链接库,不能加 .dll
TestNative test = new TestNative();
test.sayHello();
}
}
class Father {
public void function() {
System.out.println("Father function");
}
}
class Child extends Father {
public void function() {
System.out.println("Child function");
}
}
C++代码:
JNIEXPORT void JNICALL Java_com_fomagic_TestNative_sayHello (JNIEnv *env, jobject obj)
{
jclass clazz_TestNative=env->GetObjectClass(obj);
jfieldID id_p=env->GetFieldID(clazz_TestNative,"p","Lcom/fomagic/Father;");
jobject p= env->GetObjectField(obj,id_p);
jclass clazz_Father = env->FindClass("com/fomagic/Father");
jmethodID id_Father_function= env->GetMethodID(clazz_Father,"function","()V");
//env->CallVoidMethod(p,id_Father_function);;
env->CallNonvirtualVoidMethod(p,clazz_Father,id_Father_function);
}
.