上文中简单的说了一下JAVA调用C/C++的一般过程并举了几个具有代表性的小例子。本文来说一说C/C++是如何调用JAVA的,包括调用JAVA实现的类的方法,设置JAVA实现的类的成员、属性等。本文先说一下C/C++调用JAVA的一般步骤,在举几个小例子做分析说明。由于作者水平有限,难免会有错误以及说的不对、不好的地方,请不小心看到的您批评指正。
一、调用的一般步骤
a、创建虚拟机
b、获得class
c、实例化对象,构造一个java类的对象(如果是静态方法调用不需要这一步)
d、读取设置类中的属性(如果有需要)
e、调用java类中方法
基本的开发环境:
操作系统:Ubuntu12.04
GCC版本:gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)
JDK版本:java version "1.7.0_101"
二、调用的简单实现
2.1 调用静态方法
实现的功能:在java类中实现一个静态方法,这个方法向终端打印一个字符串,这个字符串是作为参数传入进来的。
java代码的实现如下:
/* define a class for JNI */
public class JNIDemo {
/* Implement a static method */
public static void printHello(String str){
System.out.println(str);
}
}
C代码的实现:
#include <stdio.h>
#include <stdlib.h>
#include <jni.h> /* 位于/usr/lib/jvm/java-1.7.0-openjdk-amd64/include */
/* 这个函数用于创建java虚拟机
* jvm : java虚拟机
* env : JNI运行环境
*/
jint create_VM(JavaVM **jvm, JNIEnv** env) {
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6; // java版本
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=./"; // 配置java类的目录为当前目录
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
return JNI_CreateJavaVM(jvm, (void **)env, &args); // 创建java虚拟机
}
/* 程序的入口函数 */
int main(int argc, char *argv[])
{
jint ret = 0;
JavaVM *jvm;
JNIEnv *env;
jclass cls;
jmethodID mid;
jstring jstr;
/* 1、创建java虚拟机 */
ret = create_VM(&jvm, &env);
if(ret < 0)
{
printf("create_VM error!\n");
return -1;
}
/* 2、获得class,要调用的java类 */
cls = (*env)->FindClass(env, "JNIDemo");
if(cls == NULL)
{
printf("FindClass error!\n");
return -1;
}
/* 3、实例化java方法 */
/* 4、调用java方法 */
jstr = (*env)->NewStringUTF(env, "Hello,world!");
mid = (*env)->GetStaticMethodID(env, cls, "printHello", "(Ljava/lang/String;)V"); // 获取方法的id
(*env)->CallStaticVoidMethod(env, cls, mid, jstr); // 调用方法
return 0;
}
编译程序的命令如下(后续几个例子相同):
javac -d . JNIDemo.java
gcc -o c_demo c_demo.c -I /usr/lib/jvm/java-7-openjdk-amd64/include -L /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server -ljvm
LD_LIBRARY_PATH=/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server ./c_demo
结果如下图所示:
从结果可以看出,C代码成功的调用了java的方法,并把字符串打印了出来。
2.2 调用非静态方法
这个例子实现的功能是:C调用java实现的方法向终端打印一个字符串并接收java方法的返回值,把这个返回值也通过终端打印出来。
java代码的实现如下:
/* define a class for JNI */
public class JNIDemo {
/* Implement a method */
public int printHello(String str){
System.out.println(str);
return 128;
}
}
C代码的实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <jni.h> /* 位于/usr/lib/jvm/java-1.7.0-openjdk-amd64/include */
/* 这个函数用于创建java虚拟机
* jvm : java虚拟机
* env : JNI运行环境
*/
jint create_VM(JavaVM **jvm, JNIEnv** env) {
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6; // java版本
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=./"; // 配置java类的目录为当前目录
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
return JNI_CreateJavaVM(jvm, (void **)env, &args); // 创建java虚拟机
}
/* 程序的入口函数 */
int main(int argc, char *argv[])
{
jint ret = 0;
JavaVM *jvm;
JNIEnv *env;
jclass cls;
jmethodID cid;
jobject obj;
jmethodID mid;
jstring jstr;
/* 1、创建java虚拟机 */
ret = create_VM(&jvm, &env);
if(ret < 0)
{
printf("create_VM error!\n");
return -1;
}
/* 2、获得class,要调用的java类 */
cls = (*env)->FindClass(env, "JNIDemo");
if(cls == NULL)
{
printf("FindClass error!\n");
return -1;
}
/* 3、实例化java方法 */
cid = (*env)->GetMethodID(env, cls, "<init>", "()V"); // 获取JNIDemo类的构造方法
if(cid == NULL)
{
printf("GetMethodID for constructor!\n");
return -1;
}
obj = (*env)->NewObject(env, cls, cid); // 构造一个JNIDemo类的实例
if(obj == NULL)
{
printf("NewObject error!\n");
return -1;
}
/* 4、调用java方法 */
jstr = (*env)->NewStringUTF(env, "Hello,world!");
mid = (*env)->GetMethodID(env, cls, "printHello", "(Ljava/lang/String;)I"); // 获得要调用的java方法
ret = (*env)->CallIntMethod(env, obj, mid, jstr); // 调用这个java方法
printf("The return value is %d\n", ret); // 打印java方法的返回值
return 0;
}
编译运行结果如下:
从上面可以看出C成功的调用了java类的非静态方法,并把java类的返回值打印了出来。
2.3 设置java类的成员属性
这个liz实现的功能:在java类中实现两个属性,在实现一个打印属性的方法,C代码调用这个类,设置它的两个属性,并把属性值打印出来。
java代码的实现如下:
/* define a class for JNI */
public class JNIDemo {
/* Define two member variables */
private int age = 0;
private String name = null;
/* Implement a method */
public void printInfo(){
System.out.println("age : " + this.age);
System.out.println("name : " + this.name);
}
}
C代码的实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <jni.h> /* 位于/usr/lib/jvm/java-1.7.0-openjdk-amd64/include */
/* 这个函数用于创建java虚拟机
* jvm : java虚拟机
* env : JNI运行环境
*/
jint create_VM(JavaVM **jvm, JNIEnv** env) {
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6; // java版本
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=./"; // 配置java类的目录为当前目录
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
return JNI_CreateJavaVM(jvm, (void **)env, &args); // 创建java虚拟机
}
/* 程序的入口函数 */
int main(int argc, char *argv[])
{
jint ret = 0;
JavaVM *jvm;
JNIEnv *env;
jclass cls;
jmethodID cid;
jobject obj;
jmethodID mid;
jfieldID fid_name;
jfieldID fid_age;
jstring jstr;
/* 1、创建java虚拟机 */
ret = create_VM(&jvm, &env);
if(ret < 0)
{
printf("create_VM error!\n");
return -1;
}
/* 2、获得class,要调用的java类 */
cls = (*env)->FindClass(env, "JNIDemo");
if(cls == NULL)
{
printf("FindClass error!\n");
return -1;
}
/* 3、实例化java方法 */
cid = (*env)->GetMethodID(env, cls, "<init>", "()V"); // 获取JNIDemo类的构造方法
if(cid == NULL)
{
printf("GetMethodID for constructor!\n");
return -1;
}
obj = (*env)->NewObject(env, cls, cid); // 构造一个JNIDemo类的实例
if(obj == NULL)
{
printf("NewObject error!\n");
return -1;
}
/* 4、设置类的属性 */
fid_name = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;"); // 获得JNIDemo类的name成员
if(fid_name == NULL)
{
printf("GetFieldID for name is failed!\n");
return -1;
}
fid_age = (*env)->GetFieldID(env, cls, "age", "I"); // 获得JNIDemo类的age成员
if(fid_age == NULL)
{
printf("GetFieldID for age is failed!\n");
return -1;
}
jstr = (*env)->NewStringUTF(env, "TECH-PRO"); // 初始化字符串的值
(*env)->SetObjectField(env, obj, fid_name, jstr); // 设置name成员为jstr
(*env)->SetIntField(env, obj, fid_age, 25); // 设置age成员为25
/* 5、调用java方法 */
mid = (*env)->GetMethodID(env, cls, "printInfo", "()V"); // 获得要调用的java方法
(*env)->CallVoidMethod(env, obj, mid); // 调用这个java方法
return 0;
}
编译并运行,结果如下:
从上图可以看出,成功的设置了java类的属性和方法,并调用了java类的成员方法。