JNI简单使用之二:C/C++调用JAVA

上文中简单的说了一下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类的成员方法。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值