jni的hello world
JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。 (百科)
第一个jni程序的步骤。
1编写一个java类,其中包含一个native修饰的方法,
public native String readFile(Strig filename);
写好java类后,使用javac编译:javac Demo1.java
现在开始编写c部分的实现,我这里使用codeblocks,gcc编译器。
编译的时候,gcc编译需要指定连接参数,网上有两种参数模式,都测试在codeblocks 使用gcc编译器通过
JNI是Java Native Interface的缩写,中文为JAVA本地调用。从Java1.1开始,Java Native Interface(JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他语言,只要调用约定受支持就可以了。 (百科)
第一个jni程序的步骤。
1编写一个java类,其中包含一个native修饰的方法,
public native String readFile(Strig filename);
native修饰的方法和接口定义看起来相同,不能有方法体。
public class Demo1 {
static {
System.loadLibrary("demolib");//静态导入库,demolib就是后来生成的dll,不过不包含.dll
}
public native String readFile(Strig filename);
public static void main(String[] arge)
{
new Demo().readFile("d:\1.txt");
}
}
写好java类后,使用javac编译:javac Demo1.java
然后使用javah Demo1 生成.h头文件。
头文件基本格式
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"//这里javah默认生成是include <jni.h>如果发现编译找不到头文件,可以改成"",但是jni必须包含在工程头文件目录下
/* Header for class Demo1 */
#ifndef _Included_Demo1
#define _Included_Demo1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Demo1
* Method: readFile
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
//这个就是给c,c++调动的函数定义
JNIEXPORT jstring JNICALL Java_Demo1_readFile
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
现在开始编写c部分的实现,我这里使用codeblocks,gcc编译器。
首先建立工程的时候,选择Dynamic Link Library 动态链接库
然后导入编译jni需要的头文件,这个jdk安装目录有提供
看图,除了选择圈中的头文件之外,还要选择上图文件夹win32中的jni_md.h头文件,一共是2个头文件,包含进工程里就行了。
c代码:
#include <stdlib.h>
#include <string.h>
#include "Demo1.h" //导入javah生成的头文件
//自定义一个文件读取函数,返回char*
char* read(const char* filename)
{
int defsize=64;
char* buf=(char*)calloc(defsize,sizeof(char));
char fc;
int pos=0;
FILE* file=fopen(filename,"rb");
while ((fc=fgetc(file))!=EOF){
buf[pos++]=fc;
if (pos==defsize){
defsize=2*defsize;
buf=realloc(buf,defsize*sizeof(char));
}
}
return buf;
}
//这个就是实现了javah生成头文件里的函数。JNIEnv是jni.h定义的结构,jstring是java中String类型到c类型的转换映射。都是定义在jni头文件中。
c返回的类型要转换成和java类型相对应。
JNIEXPORT jstring JNICALL Java_Demo1_readFile(JNIEnv *env, jobject obj, jstring jstr)
{
jbyte * temp;//定义jbyte类型字节数组
//GetStringUTFChars是JNIEnv结构里定义的函数,用来转换java的类型参数,并设置了编码格式。
temp=(*env)->GetStringUTFChars(env,jstr,NULL);
//NewStringUTF函数,转换jbyte字节数组为jstring类型,返回给java就是string
return (*env)->NewStringUTF(env,read(temp));
}
编译的时候,gcc编译需要指定连接参数,网上有两种参数模式,都测试在codeblocks 使用gcc编译器通过
1:-Wl,--kill-at
2:-Wl,--add-stdcall-alias
stdcall与_cdecl是两种不同的函数调用约定,区别在函数参数入栈的顺序,由调用函数还是被调用函数将参数弹出栈,以及产生函数修饰名的方法。
jni使用的dll库函数默认使用stdcall调用约定,gcc编译的时候可能不使用stdcall,百度说gcc默认是_cdecl约定,加上这个链接参数就可以了。
我这里选择的是第二种。然后执行java程序,就可以成功调用dll输出,如果报错提示找不到dll,jdk默认dll path路径是window系统dll路径和jdk安装目录下的bin目录,放在这里就可以正常访问,设置jdk dll path加上一个.可以在当前目前调用。