一、JNI简介 JNI:Java Native Interface,是Java语言提供的一种通用接口,用于Java代码与本地化代码的交互。所谓本地化代码是指直接编译成的与机器相关的二进制代码,而非Java字节码之类的中间代码。Windows下面的可执行文件,DLL等,Linux下面的可执行文件和SO文件等,都是二进制代码。 JNI允许Java语言编写的程序与其他语言编写的程序库(DLL, SO)或可执行文件进行互操作,包括汇编、C、C++。JNI产生的原因在于以下几种需求: (1). 你的应用程序需要使用系统相关的功能,而Java代码不支持或是难以办到。这个比较典型的是实现托盘图标,有几种现成的方案都是用的JNI做的,名字好像是叫做TrayIcon和StayOnTop。当然啦,如果是用的Java1.6,那就要另当别论了。 (2). 已有其他语言写好的类库或程序,希望Java程序可以使用它们。 (3). 出于更高的性能要求,希望使用汇编或是C/C++语言来实现部分功能。 下图出自JNI Tutorial,展示了JNI的地位: 其他的理论的东西就不多说了,JNI Tutorial讲得很清楚。强烈建议阅读。 二、JNI的开发步骤 这里以使用C++编写本地化方法实现为例,开发一个使用JNI的Demo程序,具体步骤如下所示: (1). 编写带有native方法的java类 (2). 使用javac命令编译所编写的java类 (3). 使用javah命令处理类文件,生成C/C++头文件 (4). 使用C/C++实现本地方法 (5). 将C/C++编写的文件生成动态连接库 三、Demo程序,Demo程序宣示了Java代码中调用C++的输出功能打印字符串,同时演示了使用C++的输入功能读取字符串,并使用System.out.println输出。 1. 编写带native方法的Java类 本示例中的源程序就一个Java类。如下所示: // HelloJNI.java -- 简单的JNI入门示例。 // 2007-4-5 16:41:45 public class HelloJNI { public native void displayHello(); public native void showTime(); private native String getLine(String prompt); static { System.loadLibrary("hello"); } public static void main(String[] args) throws Exception { HelloJNI hj = new HelloJNI(); System.out.println("==> Demo 1: hello"); hj.displayHello(); System.out.println("==> Demo 2: time"); hj.showTime(); System.out.println("==> Demo 3: input"); String input = hj.getLine("Type a line: "); System.out.println("User typed: " + input); } } 说明:一共3个native方法,第一个简单的Hello,第二个使用<ctime>头文件中的相关函数计算当前时间,第三个读取输入。注意static语句块: static { System.loadLibrary("hello"); } 在JVM载入HelloJNI类的时候加载动态库,System.loadLibrary()中的参数是我们要生成的动态库文件的名字,不包括扩展名,在Windows下面是hello.dll,Linux下面是hello.so,这个由Java自动识别。 2. 编译此类 3. javah HelloJNI 生成C/C++头文件,生成的头文件不用任何修改。如下所示: /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class HelloJNI */ #ifndef _Included_HelloJNI #define _Included_HelloJNI #ifdef __cplusplus extern "C" { #endif /* * Class: HelloJNI * Method: displayHello * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloJNI_displayHello (JNIEnv *, jobject); /* * Class: HelloJNI * Method: showTime * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloJNI_showTime (JNIEnv *, jobject); /* * Class: HelloJNI * Method: getLine * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_HelloJNI_getLine (JNIEnv *, jobject, jstring); #ifdef __cplusplus } #endif #endif 4. 编写C/C++实现文件,如下所示: // HelloJNIImpl.cpp -- 自己编写的实现文件 // 2007-4-5 16:52:53 #include <jni.h> #include <iostream> #include <ctime> #include <string> #include <cstdio> #include <windows.h> #include "HelloJNI.h" extern "C" { // } using namespace std; JNIEXPORT void JNICALL Java_HelloJNI_displayHello(JNIEnv * env, jobject obj) { cout << "Hello, JNI tech. This is from C++!" << endl; } JNIEXPORT void JNICALL Java_HelloJNI_showTime(JNIEnv * env, jobject obj) { time_t sec; tm t; time_t loop; cout << "时间:"; sec = time(NULL); t = *localtime(&sec); unsigned int hour = t.tm_hour; unsigned int mini = t.tm_min; unsigned int secd = t.tm_sec; if(hour < 10) { cout << "0" << hour; } else { cout << hour; } cout << ":"; if(mini < 10) { cout << "0" << mini; } else { cout << mini; } cout << ":"; if(secd < 10) { cout << "0" << secd; } else { cout << secd; } cout << endl; } JNIEXPORT jstring JNICALL Java_HelloJNI_getLine(JNIEnv * env, jobject obj, jstring prompt) { char buf[128] = {0}; const char * str = (env)->GetStringUTFChars(prompt, 0); // printf("%s", str); cout << str; (env)->ReleaseStringUTFChars(prompt, str); string buffer; getline(cin, buffer); // scanf("%s", buf); return (env)->NewStringUTF(buffer.c_str()); } 说明:JNI Tutorial中使用的居然是像下面这样的代码,env指针的使用应该有误,敬请注意。 JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) { char buf[128]; const char *str = (*env)->GetStringUTFChars(env, prompt, 0); printf("%s", str); (*env)->ReleaseStringUTFChars(env, prompt, str); ... 5. 生成本地库文件。笔者使用的VS.NET 2003中的C++编译器。VS.NET 2003命令行工具生成DLL的命令: cl -I<头文件目录1> -I<头文件目录2> -LD <C/C++源文件名> -Fe<生成的DLL文件名> -I选项指定头文件目录,-LD指定C++源文件,-Fe指定生成的DLL文件名。将各个部分替换成实际的情况,实际使用的命令如下: cl -IC:/java/jdk1.5.0_10/include -IC:/java/jdk1.5.0_10/include/win32 -LD HelloJNIImpl.cpp -Fehello.dll 注:cl可以增加-EHsc参数来减少生成过程中的输出信息。如下图所示: 6. 运行结果,如下图所示: 注意自己输入的那一行! 7. 自动化。其实步骤都很简单,所以可以使用Ant来完成。下面提供一个ANT Buildfile。使用之前请根据自己的实际情况修改相关属性。 注意:Builfile编码是UTF-8的。 build.xml <?xml version="1.0" encoding="UTF-8"?> <project default="help" basedir="." > <property value="."></property> <!-- 设置头文件的目录,根据JDK具体的安装目录而定 --> <property value="C:/java/jdk1.5.0_10/include"></property> <property value="C:/java/jdk1.5.0_10/include/win32"></property> <!-- 设置cl工具的路径,视具体情况而定 --> <property value="C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/bin"> </property> <property value="HelloJNIImpl.cpp"></property> <property value="hello.dll"></property> <target description=" ==> 编译Java源文件"> <javac srcdir="${app.home}" destdir="${app.home}"> </javac> </target> <target description=" ==> 生成C/C++头文件"> <javah destdir="${app.home}" force="yes" /> </target> <target description=" ==> 调用cl命令生成相关的DLL文件"> <exec dir="${app.home}" executable="${vc.bin.dir}/cl.exe" os="Windows XP"> <!-- 参数-EHsc好像是VS.NET2003中cl.exe的,可以不加,参考下面的注释行 --> <arg line="-EHsc -I${h.dir1} -I${h.dir2} -LD ${cpp.name} -Fe${dll.name}"/> <!-- <arg line="-I${h.dir1} -I${h.dir2} -LD ${cpp.name} -Fe${dll.name}"/> --> </exec> </target> <target description=" ==> 运行"> <java classname="HelloJNI"></java> </target> <target description=" ==> 自动进行上面任务列表"> <antcall target="compile"/> <antcall target="javah"/> <antcall target="dll"/> <antcall target="run"/> </target> <target description=" ==> 显示帮助信息"> <echo>用法帮助:</echo> <echo>ant compile 编译Java源文件</echo> <echo>ant javah 生成C/C++头文件</echo> <echo>ant dll 调用cl命令生成相关的DLL文件</echo> <echo>ant run 运行</echo> <echo></echo> <echo>ant all 自动进行上面任务列表</echo> <echo>ant help 显示帮助信息</echo> </target> </project> Done!^_^! JNI还有很多高级的功能,这里我就不说了,一来是不太会,二来也没什么意义。JNI Tutorial上讲得很全面,不错。 |