jni实现C语言调用Java程序

来源:互联网 发布:胜达seo 编辑:程序博客网 时间:2024/05/16 12:48

1 jni简介

JavaNative Interface(JNI)是Java语言的本地编程接口,是J2SDK的一部分。在java程序中我们可以通过JNI实现一些用java语言不便实现的功能。通常有以下几种情况我们需要使用JNI来实现。

标准的java类库没有提供你的应用程序所需要的功能,通常这些功能是平台相关的你希望使用一些已经有的类库或者应用程序,而他们并非用java语言编写的程序的某些部分对速度要求比较苛刻,你选择用汇编或者c语言来实现并在java语言中调用他们。

JNI既可以实现Java调用本地库, 也可以实现C/C++调用Java代码, 从而实现了两种语言的互通,可以让我们更加灵活的使用。

本博客主要介绍的是用jni实现C/C++调用Java代码。

 

2 jni环境配置

2.1安装jdk

我的linux是RedHat Enterprise linux 5,内核版本2.6.18。在Linux系统中安装Java比较简单。可以访问Java download网站或自由软件库等,选择你所有安装的操作系统类型(Linux,Linux AMD64,Solaris等)。一旦你已经选择下载文件──要么是自解压缩执行文件,要么是自解压缩的RPM文件,你都可以安装它。我下载的是jdk-1_5_0_06-linux-i586.bin。下面是安装示例:

# mkdir /usr/local/java

# cd /usr/local/java

# cp /home/soft/jdk-1_5_0_06-linux-i586.bin ./

# chmod u+x jdk-1_5_0_06-linux-i586.bin

# ./jdk-1_5_0_06-linux-i586.bin

运行完后生成jdk1.5.0_06目录,jdk被安装在/usr/local/java/jdk1.5.0_06/。

2.2设置环境变量

配置示例如下:

JAVA_HOME=/usr/local/java/jdk1.5.0_06

PATH=$PATH:/usr/local/java/jre1.5.0_05/bin

export JAVA_HOME PATH

export JRE_HOME=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JRE_HOME/lib/i386:$JRE_HOME/lib/i386/client

注意JRE_HOME的配置,若机器上没有jre环境,则安装jre,安装方法类似安装jdk

在/etc/profile文件中增加以上配置后,执行source /etc/profile命令。

 

 

3 jni实现C语言调用Java程序样例

编写java程序


编译java程序

执行javac Sample.java

 

编写C语言程序

Sample.c

 

编译C语言程序

执行gcc -o sample Sample.c -I$JAVA_HOME/include -I$JAVA_HOME/include/linux-L$JRE_HOME/lib/i386/client -ljvm

 

执行程序

./sample

 

此时出现结果:

Result of sayHello: Hello, World!

Result of sayHello: Hello,icejoywoo!

 

4 jni实现C语言调用Java程序过程详解

上面样例这段代码大概做了这几件事

l  创建虚拟机JVM, 在程序结束的时候销毁虚拟机JVM

与虚拟机创建相关的有这样几个变量:

1)  JavaVMOption options[1];

2)  JNIEnv *env;

3)  JavaVM *jvm;

4)  JavaVMInitArgs vm_args;

JavaVM就是我们需要创建的虚拟机实例;

avaVMOption相当于在命令行里传入的参数;

JNIEnv在Java调用C/C++中每个方法都会有的一个参数, 拥有一个JNI的环境

JavaVMInitArgs就是虚拟机创建的初始化参数, 这个参数里面会包含JavaVMOption

下面就是创建虚拟机

options[0].optionString ="-Djava.class.path=.";

memset(&vm_args, 0, sizeof(vm_args));

vm_args.version = JNI_VERSION_1_4;

vm_args.nOptions = 1;

vm_args.options = options;

// 启动虚拟机

status = JNI_CreateJavaVM(&jvm,(void**)&env, &vm_args);

 

 "-Djava.class.path=."就是传入当前路径, 作为JVM寻找class的用户自定义路径, 我们的Sample.class就在当前路径(用户根据实际情况进行修改).

vm_args.version是Java的版本, 这个应该是为了兼容以前的JDK, 可以使用旧版的JDK, 这个宏定义是在jni.h中, 有以下四种

#define JNI_VERSION_1_1 0x00010001

#define JNI_VERSION_1_2 0x00010002

#define JNI_VERSION_1_4 0x00010004

#define JNI_VERSION_1_6 0x00010006

 

vm_args.nOptions的含义是, 你传入的options有多长, 我们这里就一个, 所以是1。

vm_args.options = options把JavaVMOption传给JavaVMInitArgs里面去。

然后就是启动虚拟机了status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args).。

可以通过这个返回值status , 知道虚拟机是否启动成功,返回值有以下几种:

#define JNI_OK           0                 /* success */

#define JNI_ERR          (-1)              /* unknown error */

#define JNI_EDETACHED    (-2)              /* thread detached from the VM */

#define JNI_EVERSION     (-3)              /* JNI version error */

#define JNI_ENOMEM       (-4)              /* not enough memory */

#define JNI_EEXIST       (-5)              /* VM already created */

#define JNI_EINVAL       (-6)              /* invalid arguments */

 

l  寻找class对象

JVM在Java中都是自己启动的, 在C/C++中只能自己来启动了, 启动完之后的事情就和在Java中一样了, 不过要使用C/C++的语法.

获取class对象比较简单, FindClass(env, className).

cls = (*env)->FindClass(env,"Sample");

 

l  创建class对象的实例

在Java中的类名格式是java.lang.String, 但是className的格式有点不同, 不是使用'.'作为分割, 而是'/', 即java/lang/String.

我们知道Java中构造函数有两种, 一种是默认的没有参数的, 一种是自定义的带有参数的. 对应的在C/C++中, 有两种调用构造函数的方法.

调用默认构造函数

// 调用默认构造函数

obj = (*env)->AllocObjdect(env,cls); 

构造函数也是方法, 类似调用方法的方式.

// 调用指定的构造函数, 构造函数的名字叫做<init>

mid = (*env)->GetMethodID(env, cls,"<init>", "()V");

obj = (*env)->NewObject(env, cls, mid);

调用方法和修改属性

关于方法和属性是有两个ID与之对应, 这两个ID用来标识方法和属性.

jmethodID mid;

jfieldID fid;

方法分为静态和非静态的, 所以对应的有

 

mid =(*env)->GetStaticMethodID(env, cls, "sayHello","(Ljava/lang/String;)Ljava/lang/String;");

mid =(*env)->GetMethodID(env, cls, "sayHello","()Ljava/lang/String;");

上面两个方法是同名的, 都叫sayHello, 但是签名不同,所以可以区分两个方法。签名可以通过执行下面的命令查看:

javap-s -private Sample

 

l  调用方法和修改属性

JNI的函数都是有一定规律的, Static就表示是静态, 没有表示非静态。

方法的调用如下

jstring result =(jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg);

jstring result =(jstring)(*env)->CallObjectMethod(env, obj, mid);

 

我们可以看到静态方法是只需要class对象, 不需要实例的, 而非静态方法需要使用我们之前实例化的对象.

属性也有静态和非静态, 示例中只有非静态的。

获取属性ID

fid =(*env)->GetFieldID(env, cls, "name","Ljava/lang/String;");

修改属性的值

(*env)->SetObjectField(env, obj, fid,arg); // 修改属性

 

关于jstring的说明

java的String都是使用了unicode, 是双字节的字符, 而C/C++中使用的单字节的字符。

从C转换为java的字符, 使用NewStringUTF方法

jstringarg = (*env)->NewStringUTF(env, name);

从java转换为C的字符, 使用GetStringUTFChars

constchar* str = (*env)->GetStringUTFChars(env, result, 0);

5 java与c之间的数据交互

5.1 java向c传递基本数据类型

对于基本数据类型,java和c是相互对应的,所以可以直接使用。它们的对应关系为;

JNI类型映射

Java类型

本地类型

描述

boolean

jboolean

C/C++8位整型

byte

jbyte

C/C++带符号的8位整型

char

jchar

C/C++无符号的16位整型

short

jshort

C/C++带符号的16位整型

int

jint

C/C++带符号的32位整型

long

jlong

C/C++带符号的64位整型e

float

jfloat

C/C++32位浮点型

double

jdouble

C/C++64位浮点型

Object

jobject

任何Java对象,或者没有对应java类型的对象

Class

jclass

Class对象

String

jstring

字符串对象

Object[]

jobjectArray

任何对象的数组

boolean[]

jbooleanArray

布尔型数组

byte[]

jbyteArray

比特型数组

char[]

jcharArray

字符型数组

short[]

jshortArray

短整型数组

int[]

jintArray

整型数组

long[]

jlongArray

长整型数组

float[]

jfloatArray

浮点型数组

double[]

jdoubleArray

双浮点型数组

void

void

n/a

 

5.2 C中返回一个字符串

       ...................

 

     (*env)->NewStringUTF(env,"Huazi华仔");

 

      ...................

 

5.3 C中返回一个数组

    .....................

 

    int i = 0;

 

    jintArray array;

 

    array = (*env)->NewIntArray(env,8);

 

    for(;i<8;i )

 

{

 

  // 赋值成 0 ~ 7

 

       (*env)->SetObjectArrayElement(env,array,i,i);

 

    }

 

    return array;

 

 

5.4 C中使用调用传入的参数是数组array

    .........

 

   int sum =0, i;

 

   int len = (*env)->GetArrayLength(env,array);

 

   jint *element =(*env)->GetIntArrayElement(env,array,0);

 

   for(i=0;i<len;i )

 

   {

 

       sum = *(element i);

 

   }

 

   return sum;

 

5.5 C中调用java中类的方法 没有参数 只有返回值String

 

  @" ()Ljava/lang/String;" 表示参数为空 返回值是String类型

 

  JNIEXPORT jstring JNICALLJava_com_huazi_Demo_getCallBack(JNIENV env,jobject object){

 

    jmethodID mid;

 

    jclass cls =(*env)->FindClass(env,"com/huazi/Demo"); //后面是包名 类名

 

   mid =(*env)->GetMethodID(env,cls,"TestMethod","()Ljava/lang/String;");//TestMethod java中的方法名

 

   jstring msg =(*env)->CallObjectMethod(env,object,mid); //object 注意下是jni传过来的jobject

 

   return msg;

 

}

 

5.6 C中调用java中类的静态方法 没有参数 只有返回值String

 

  @"()Ljava/lang/String;" 表示参数为空返回值是String类型

 

  JNIEXPORT jstring JNICALL Java_com_huazi_Demo_getCallBack(JNIENVenv,jobject object){

 

    jmethodID mid;

 

    jclass cls =(*env)->FindClass(env,"com/huazi/Demo"); //后面是包名 类名

 

   mid =(*env)->GeStatictMethodID(env,cls,"TestMethod","()Ljava/lang/String;");// TestMethod java中的方法名

 

   jstring msg =(*env)->CallStaticObjectMethod(env,cls,mid); //object 注意下是jni传过来的jobject

 

   return msg;

 

}

 

5.7 C中调用java中类的方法 二个参数 第一个参数是int 第二个参数是String 返回值是String

 

  "(ILjava/lang/String;)Ljava/lang/String" 表示参数是第一个参数是整形,第二个参数是String ,返回值是String

 

  JNIEXPORT jstring JNICALLJava_com_huazi_Demo_getCallBack(JNIENV env,jobject object){

 

    jmethodID mid;

 

    jclass cls =(*env)->FindClass(env,"com/huazi/Demo"); //后面是包名 类名

 

   mid = (*env)->GeStatictMethodID(env,cls,

 

"TestMethod","(ILjava/lang/String;)Ljava/lang/String;");// TestMethod java中的方法名

 

   jstring param =(*env)->NewStringUTF(env,"huazi");

 

   jstring msg =(*env)->CallStaticObjectMethod(env,cls,mid,25,param); //object 注意下是jni传过来的jobject

 

   return msg;

 

}

 

5.8 C中调用java中的全局变量   

 

   jclass cls = (*env)->FindClass(env,"com/huazi/Demo");

 

   jfieldID id =(*env)->GetFieldID(env,cls,"num","I"); //num 为java中的变量 I表示这个变量的类型是整形

 

   jint param =(*env)->GetIntField(env,object,id);

 

   jfieldID id2 =(*env)->GetFieldID(env,cls,"num2","Ljava/lang/String");//num2 为java中的变量 Ljava/lang/String表示这个变量的类型是String

 

   jstring param2 =(*env)->GetObjectField(env,object,id2);

 

5.9 C中调用java中的静态的变量

 

   jclass cls =(*env)->FindClass(env,"com/huazi/Demo");

 

   jfieldID id =(*env)->GetStaticFieldID(env,cls,"num","Ljava/lang/String");//num 为java中的静态变量 Ljava/lang/String 表示这个变量的类型是String

 

   jstring param =(*env)->GeStaticObjectField(env,cls,id);

 

0 0
原创粉丝点击