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程序样例
l 编写java程序
l 编译java程序
执行javac Sample.java
l 编写C语言程序
Sample.c
l 编译C语言程序
执行gcc -o sample Sample.c -I$JAVA_HOME/include -I$JAVA_HOME/include/linux-L$JRE_HOME/lib/i386/client -ljvm
l 执行程序
./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);
- jni实现C语言调用Java程序
- android studio 调用jni,实现Java调用C语言程序
- JNI实现Java调用C/C++语言
- C语言调用Java JNI
- JNI-->Java调用C语言
- JNI-->C调用Java语言
- java jni 调用C语言
- JNI实现java调用C语言实现底层代码
- java JNI调用c程序
- 【Java】Java调用C/C++程序的实现(jni)
- JNI小程序:Java通过JNI调用C程序
- JNI------Java调用C语言中的MessageBox
- JNI学习------C语言调用Java
- JNI学习------C语言调用Java
- java 使用JNI调用C语言
- Java通过JNI调用C语言
- Java通过jni调用c语言方法
- java jni 调用c语言函数
- BI工具:tableau桌面版 视频学习笔记(三、聚合)
- C++学习笔记 --- 析构函数
- PAT (Advanced) 1078. Hashing (25)
- codeforces224A
- JSTL的forEach标签和catch标签
- jni实现C语言调用Java程序
- 如何用svn进行更新和提交
- qf_pool.c
- cfor和ceatch和choose
- 关于NS2所引用的几个静态链接库文件
- android中延迟执行某个任务
- c/c++ 替换文本文件中的字符串函数【方法二】linux、mac版本
- iphone定位 基本知识
- AP多根天线,多入多出的问题