首页 > 代码库 > 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 }
View Code

  这次,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
View Code

  我们发现,通过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 }
View Code

    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)

Java VM Type Signatures
Type SignatureJava Type
Zboolean
Bbyte
Cchar
Sshort
Iint
Jlong
Ffloat
Ddouble
L fully-qualified-class ;fully-qualified-class
[ typetype[]
( arg-types ) ret-typemethod 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 }
View Code

    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++的相互调用.