JAVA快速入门-JNI_C调用JAVA,
分享于 点击 25247 次 点评:129
JAVA快速入门-JNI_C调用JAVA,
上一篇实现了 JAVA 调用 C 语言函数的过程,当然 C 也能通过 JNI 来访问 JAVA 的方法和成员变量,实际上具体调用什么函数以及如何实现这些具体的操作,都在 jni 的手册中有进行讲解。下面以程序来实现 C 调用 JAVA 方法或成员变量的过程。先是一个简单的访问 JAVA 静态成员方法的程序
JAVA代码:
public class Hello {
/* C来访问这个静态成员方法 */
public static void main(String args[]){
System.out.println("Hello World!");
}
}
C代码:
#include <stdio.h>
#include <jni.h>
/* 创建 java 虚拟机 */
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);
}
int main(int argc, char **argv)
{
JavaVM* jvm;
JNIEnv* env;
jclass cls;
int ret = 0;
jmethodID mid;
/* 1. 创建 java 虚拟机 */
if(create_vm(&jvm, &env)){
printf("can not create jvm\n");
return -1;
}
/* 2. 获取 JAVA 里面的类 */
cls = (*env)->FindClass(env, "Hello");
if (cls == NULL) {
printf("can not find hello class\n");
ret = -1;
goto destroy;//返回错误
}
/* 3. 获得一个静态方法的ID */
/* 后面的字段描述符可以使用 javap -p -s Hello.class 来获得 */
mid = (*env)->GetStaticMethodID(env, cls, "main","([Ljava/lang/String;)V");
if (mid == NULL) {
ret = -1;
printf("can not get method\n");
goto destroy;
}
/* 4. 调用这个静态方法 */
(*env)->CallStaticVoidMethod(env, cls, mid, NULL);
destroy:
(*jvm)->DestroyJavaVM(jvm);
return ret;
}
如何去编译上面这段代码呢,需要用到如下编译指令:
- javac Hello.java
- javap -p -s Hello.class 这个指令用于获得字段描述符该如何写
- gcc -o caller caller.c -I/usr/lib/jvm/java-1.8.0-openjdk-amd64/include -L /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/amd64/server -ljvm
- LD_LIBRARY_PATH=/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/amd64/server ./caller
上面的代码可以实现直接调用 JAVA 里面写好的 main 方法,但是此时的 main 方法是一个 static 类型的方法,如果想调用非 static 方法需要做哪些改动呢,这里在 JAVA 里面再实现一个非 static 的方法然后调用,具体看代码实现。
JAVA 代码:
public class Hello {
public static void main(String args[]){
System.out.println("Hello World!");
}
/* C 里面调用这个非 static 的方法 */
public int sayhello_to(String name){
System.out.println("Hello, "+name);
return 123;
}
}
C代码:
#include <stdio.h>
#include <jni.h>
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);
}
int main(int argc, char **argv)
{
JavaVM* jvm;
JNIEnv* env;
jclass cls;
int ret = 0;
jmethodID mid;
jmethodID cid;
jobject jobj;
jstring jstr;
int r = 0;
/* 1. 创建java虚拟机 */
if(create_vm(&jvm, &env)){
printf("can not create jvm\n");
return -1;
}
/* 2. 获取JAVA里面的 Hello 类 */
cls = (*env)->FindClass(env, "Hello");
if (cls == NULL) {
printf("can not find hello class\n");
ret = -1;
goto destroy;
}
/* 3. 获得一个构造方法的ID */
/* 由于sayhello_to不是静态方法,所以这里需要去获得一个构造方法 */
/* 获取 Hello 类的构造方法ID,这个构造方法不需要参数“()V” */
cid = (*env)->GetMethodID(env, cls,"<init>", "()V");
if (cid == NULL) {
ret = -1;
printf("can not get constructor method\n");
goto destroy;
}
/* 3.1 构造方法里实例一个对象,因为调用的方法非static */
jobj = (*env)->NewObject(env, cls, cid);
if (jobj == NULL) {
ret = -1;
printf("can not create object\n");
goto destroy;
}
/* 4. 获得 sayhello_to 方法的ID */
mid = (*env)->GetMethodID(env, cls, "sayhello_to","(Ljava/lang/String;)I");
if (mid == NULL) {
ret = -1;
printf("can not get method\n");
goto destroy;
}
/* 4.1 构造字符串参数 */
jstr = (*env)->NewStringUTF(env, "this is string");
/* 4.2 调用方法 */
/* 调用 jobj 里面的 mid 方法,并传入一个 jstr参数 */
r = (*env)->CallIntMethod(env, jobj, mid, jstr);
printf("ret = %d\n", r);//打印JAVA方法的返回值
destroy:
(*jvm)->DestroyJavaVM(jvm);
return ret;
}
上面程序过程实现了 C 调用 JAVA 里面的非静态成员方法的过程,那么有办法去读取或设置 JAVA 类的中成员变量呢,看下一个代码,实现去调用和设置类里面的成员变量。
JAVA 代码:
public class Hello {
/* 定义两个成员变量 */
private String name;
private int age;
public static void main(String args[]){
System.out.println("Hello World!");
}
/* 由C调用变量和设置变量后对其进行打印 */
public int sayhello_to(String name){
System.out.println("Hello, "+name+"!I am "+this.name+", "+age+" years old.");
return 123;
}
}
C代码:
#include <stdio.h>
#include <jni.h>
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);
}
int main(int argc, char **argv)
{
JavaVM* jvm;
JNIEnv* env;
jclass cls;
int ret = 0;
jmethodID mid;
jmethodID cid;
jobject jobj;
jstring jstr;
jfieldID nameID;
jfieldID ageID;
int r = 0;
/* 1. 创建java虚拟机 */
if(create_vm(&jvm, &env)){
printf("can not create jvm\n");
return -1;
}
/* 2. 获取JAVA里面的类 */
cls = (*env)->FindClass(env, "Hello");
if (cls == NULL) {
printf("can not find hello class\n");
ret = -1;
goto destroy;
}
/* 3. 获得一个构造方法的ID */
cid = (*env)->GetMethodID(env, cls,"<init>", "()V");
if (cid == NULL) {
ret = -1;
printf("can not get constructor method\n");
goto destroy;
}
/* 3.1 构造方法里实例一个对象,因为调用的方法非static */
jobj = (*env)->NewObject(env, cls, cid);
if (jobj == NULL) {
ret = -1;
printf("can not create object\n");
goto destroy;
}
/* 3.2 获得 name 属性ID */
nameID = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
if(nameID == NULL){
ret = -1;
printf("can not get field name");
goto destroy;
}
/* 3.3 读取/设置 name 字符串为“Bill”*/
jstr = (*env)->NewStringUTF(env, "Bill");//实例一个字符串
(*env)->SetObjectField(env, jobj, nameID, jstr);//把name设置为刚创建的Bill
/* 3.4 获得 age 属性ID */
ageID = (*env)->GetFieldID(env, cls, "age", "I");
if(ageID == NULL){
ret = -1;
printf("can not get field age");
goto destroy;
}
/* 3.5 读取/设置 age 整形变量为 10*/
(*env)->SetIntField(env, jobj, ageID, 10);
/* 4. 获得 sayhello_to 方法的ID */
mid = (*env)->GetMethodID(env, cls, "sayhello_to","(Ljava/lang/String;)I");
if (mid == NULL) {
ret = -1;
printf("can not get method\n");
goto destroy;
}
/* 4.1 构造字符串参数 */
jstr = (*env)->NewStringUTF(env, "this is string");
/* 4.2 调用方法 */
/* 调用 jobj 里面的 mid 方法,并传入一个 jstr参数 */
r = (*env)->CallIntMethod(env, jobj, mid, jstr);
printf("ret = %d\n", r);//打印JAVA方法的返回值
destroy:
(*jvm)->DestroyJavaVM(jvm);
return ret;
}
最后以思维导图做个总结:
相关文章
- 暂无相关文章
用户点评