首页 > 代码库 > java JNI 的实现(2)-java和C/C++的相互调用.
java JNI 的实现(2)-java和C/C++的相互调用.
目录
概述
一,java代码
二,稍微注意通过javah生成的‘C/C++‘.h头文件和源java代码的关系
三,在C/C++中实现java的native方法(完整C/C++)
1,修改age,即Java_juk_demo_JSay_changeAge方法
2,调用java中的‘javaStatic‘方法,和上面C/C+实现方法相似
3,最后测试
概述
从第一篇java JNI 的实现(1)-又进一步加深对JVM实现的理解对JNI有了大致的理解之后,并知道JNI中java如何调用C/C++等,这篇文章我们进一步将JNI中,java和C/C++的相互调用.
一,java代码:
我们稍微对类做了更改,如下:
1 package juk.demo; 2 3 public class JSay { 4 //member/class field. 5 public static String country = "cn"; 6 7 public int age = 20; 8 9 //native method.10 public native void changeAge();11 12 public static native void staticSay();13 14 //member method.15 public void getVoid() {16 17 }18 19 public String getString() {20 return "json";21 }22 23 public int getInt() {24 return 1;25 }26 27 private void getUnknown() {28 29 }30 31 public int[] getIntArray() {32 return new int[]{1,2};33 }34 35 //static method.36 public static void javaStatic() {37 System.out.println(country + ", center china");;38 }39 40 static {41 System.loadLibrary("JNITestCCPP");42 }43 44 45 public static void main(String[] args) {46 //invoke native method.47 JSay.staticSay();48 49 JSay jSay = new JSay();50 System.out.println("before invoke native,age=" + jSay.age);51 jSay.changeAge();52 System.out.println("after invoke native,age=" + jSay.age);53 54 }55 56 }
这次,java代码有两个native方法(changeAge和staticSay),并且有许多成员和类的普通java方法(主要是研究通过javah产生的.h头文件和源java代码直接的关系),
我们这次的目的,一是在java中调用‘changeAge‘ native方法的时候,在C/C++代码中对其对象的age属性进行更改(当然,一般成员变量都是封装的,这里只是为了测试);二是在java调用‘staticSay‘ native方法的时候,在C/C++代码中,反过来调用java的‘javaStatic‘方法.
二,稍微注意通过javah生成的‘C/C++‘.h头文件和源java代码的关系:
1 /* DO NOT EDIT THIS FILE - it is machine generated */ 2 #include "jni.h" 3 /* Header for class juk_demo_JSay */ 4 5 #ifndef _Included_juk_demo_JSay 6 #define _Included_juk_demo_JSay 7 #ifdef __cplusplus 8 extern "C" { 9 #endif10 /*11 * Class: juk_demo_JSay12 * Method: changeAge13 * Signature: ()V14 */15 JNIEXPORT void JNICALL Java_juk_demo_JSay_changeAge16 (JNIEnv *, jobject);17 18 /*19 * Class: juk_demo_JSay20 * Method: staticSay21 * Signature: ()V22 */23 JNIEXPORT void JNICALL Java_juk_demo_JSay_staticSay24 (JNIEnv *, jclass);25 26 #ifdef __cplusplus27 }28 #endif29 #endif
我们发现,通过javah命令生成的C/C++.h头文件,不会在.h头文件中出现(这其实也是很合情理的,但是我们却可以通过java中的native方法对应的.h中的方法的参数‘JNIEnv *‘去获得java中所有的成员).
三,在C/C++中实现java的native方法(完整C/C++):
1 #include <iostream> 2 #include "juk_demo_JSay.h" 3 using namespace std; 4 5 //3 ways to get jclass: 6 /* 7 1,(JNIEnv *)->FindClass(class-name),which find the class by the classpath. 8 2,(JNIEnv *)->GetObjectClass(jobject). 9 3,(JNIEnv *)->GetSuperclass(jobject).10 */11 12 /*13 * Class: juk_demo_JSay14 * Method: changeAge15 * Signature: ()V16 */17 JNIEXPORT void JNICALL Java_juk_demo_JSay_changeAge18 (JNIEnv *env, jobject obj) {19 jclass JSayClass = env->GetObjectClass(obj);20 jfieldID ageId = env->GetFieldID(JSayClass, "age", "I");21 //env->GetIntField(obj, ageId);22 env->SetIntField(obj, ageId, 22); //set the age with a new value.23 }24 25 /*26 * Class: juk_demo_JSay27 * Method: staticSay28 * Signature: ()V29 */30 JNIEXPORT void JNICALL Java_juk_demo_JSay_staticSay31 (JNIEnv *env, jclass clz) {32 //jclass JSayClass = env->FindClass("juk.demo.JSay");33 34 //javaStatic method.35 //the third argument,can be fetch with ‘javap‘ command.36 jmethodID getStaticMethodId = env->GetStaticMethodID(clz, "javaStatic", "()V");37 env->CallStaticVoidMethod(clz, getStaticMethodId, NULL);38 39 //get age field.40 /*jfieldID countryId = env->GetStaticFieldID(JSayClass, "country", "Ljava/lang/String;");41 env->GetStaticObjectField(JSayClass, countryId);*/42 }
1,修改age,即Java_juk_demo_JSay_changeAge方法,我们首先要获得jclass对象(jclass对应于java中的Class对象),而jclass对象的获得可以根据3中方式:
1,(JNIEnv *)->FindClass(class-name),which find the class by the classpath.2,(JNIEnv *)->GetObjectClass(jobject).3,(JNIEnv *)->GetSuperclass(jobject).
然后可以通过(JNIEnv *)获得属性的ID,并修改.稍微注意的是‘env->GetFieldID‘方法中的第3个参数,是为了区别方法的重载问题,
如下表(来自oracle的Chapter 3: JNI Types and Data Structures)
Type Signature | Java Type |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
L fully-qualified-class ; | fully-qualified-class |
[ type | type[] |
( arg-types ) ret-type | method type |
sun为了方便我们程序员,写一个工具(即,jdk中的javap),而不用我们记忆那些类型签名,例如,我们可以在包含该java的class的目录上(通过命令行),输入 ‘javap -s -public juk.demo.JSay‘,即可得到对应的签名,如:
1 Compiled from "JSay.java" 2 public class juk.demo.JSay extends java.lang.Object{ 3 public static java.lang.String country; 4 Signature: Ljava/lang/String; 5 public int age; 6 Signature: I 7 public juk.demo.JSay(); 8 Signature: ()V 9 public native void changeAge();10 Signature: ()V11 public static native void staticSay();12 Signature: ()V13 public void getVoid();14 Signature: ()V15 public java.lang.String getString();16 Signature: ()Ljava/lang/String;17 public int getInt();18 Signature: ()I19 public int[] getIntArray();20 Signature: ()[I21 public static void javaStatic();22 Signature: ()V23 public static void main(java.lang.String[]);24 Signature: ([Ljava/lang/String;)V25 }
2,调用java中的‘javaStatic‘方法,和上面C/C+实现方法相似,不过由于在java源码中,native被定义成了类方法,所以不用获得jclass,而直接从参数获取.同理,通过(JNIEnv *)获得java中的javaStatic方法,并调用.
最后,我们再java主函数中进行测试,结果为:
cn, center chinabefore invoke native,age=20after invoke native,age=22
java JNI 的实现(2)-java和C/C++的相互调用.