Jni使用总结二
c程序调用java
本文属于原创,转载请声明。
前言
众所周知,Java语言是将源代码编译成class文件的,并不能直接运行,需要在虚拟机中解析之后才能运行。个人理解jni的作用就是在我们的C程序中创建虚拟机,然后将java程序加载进来运行,根据我们的需求使用类的属性和方法。
1.java 程序
我们在c函数中将会对 age
属性进行设置和调用sayhello
方法并传入name
代码没啥可以说的,重点在c程序。
public class Hello{
private int age;
public static void main(String[] args) {
System.out.println("Hello world!");
}
public int sayhello(String name){
System.out.println("Hello world!"+name);
System.out.println("Hello world!"+age);
return the data from java to c;
}
public void sayhello(){
}
}
2.c 程序
a.创建虚拟机
写成函数的形式在main函数中调用。重点在与使用JNI_CreateJavaVM
函数创建虚拟机
jint create_vm(JavaVM** jvm,JNIEnv** env) {
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=./"; //指明什么目录下查找类
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
return JNI_CreateJavaVM(jvm, (void **)env, &args);
}
//main
JavaVM* jvm;
JNIEnv* env;
if(create_vm(&jvm,&env))
{
printf("create vm error\n");
return -1;
}
b.查找获取类
使用FindClass
查找我们在c程序中要调用的类,第二个参数指定要查找的类名
jclass cls;
cls = (*env)->FindClass(env, "Hello");
if (cls == NULL) {
printf("can not find class\n");
ret=-1;
goto destroy;
}
c.实例化对象
实例化对象包括以下几个小点:
i.获取构造方法ID
//获取构造方法ID
cid = (*env)->GetMethodID(env, cls,"<init>", "()V");
if (cid == NULL){
ret=-1;
printf("can not get the construction method\n");
goto destroy;
}
使用GetMethodID
获取构造方法 :
第一个参数:环境env
第二个参数:上文查找到的类cls
第三个参数:默认<init>
第四个参数:JNI字段描述符
通过命令获取
javap -p -s Hello
使用javap生成如下:Signature
就是JNI字段描述符
public class Hello {
private int age;
Signature: I
public Hello();
Signature: ()V
public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
public int sayhello(java.lang.String);
Signature: (Ljava/lang/String;)I
public void sayhello();
Signature: ()V
}
ii.创建参数
注 : 如果调用没有参数的构造方法,此步骤可忽略。
iii.获取实例化对象
使用NewObject
传入环境env
,要实例化的类
,类的构造方法ID
,返回类的实例化对象。
//获取实例化对象
jobj= (*env)->NewObject(env, cls,cid);
if (cid == NULL){
ret=-1;
printf("can not create object\n");
goto destroy;
}
d.设置类的属性
上文讲到要设置类的属性age的值,需要先通过GetFieldID
找到属性的ID,才能使用SetIntField
进行读取或者设置。
GetFieldID
的用法类似GetMethodID
SetTypeField
根据属性的类型修改Type的值。
//设置类的属性
int age;
jfid = (*env)->GetFieldID(env, cls, "age", "I");
if (jfid == NULL){
ret=-1;
printf("can not get field ID\n");
goto destroy;
}
age=22;
(*env)->SetIntField(env, jobj, jfid, age);//env,实例化对象,属性ID,要设置的内容
e.调用方法
i.获得调用方法的ID
//获得调用方法的ID
mid = (*env)->GetMethodID(env, cls, "sayhello","(Ljava/lang/String;)I");
if(mid==NULL)
{
printf("can not get the method\n");
goto destroy;
}
ii.调用方法
CallTypeMethod
根据Java方法的返回值类型进行修改,参数分别是:
环境env
;
实例化对象
;
方法ID
;
参数
;
//调用方法
result = (*env)->CallIntMethod(env, jobj, mid, jstr);
printf("%d\n",result );
总结
上面两篇文章,介绍了java程序怎么调用c/c++库的函数;c程序怎么调用java方法;主要是从简单入手,希望起到抛砖引玉的作用。但文中对数据类型的转换没有详细的介绍,只是举例。期待第三篇对着方面进行详细的介绍。分享即所得。–manjia