JNI示例
一、JNI简介
JNI:JavaNativeInterface,是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的开发步骤
这里以使用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-516:41:45
publicclassHelloJNI{
publicnativevoiddisplayHello();
publicnativevoidshowTime();
privatenativeStringgetLine(Stringprompt);
static{
System.loadLibrary("hello");
}
publicstaticvoidmain(String[]args)throwsException{
HelloJNIhj=newHelloJNI();
System.out.println("==>Demo1:hello");
hj.displayHello();
System.out.println("==>Demo2:time");
hj.showTime();
System.out.println("==>Demo3:input");
Stringinput=hj.getLine("Typealine:");
System.out.println("Usertyped:"+input);
}
}
说明:一共3个native方法,第一个简单的Hello,第二个使用<ctime>头文件中的相关函数计算当前时间,第三个读取输入。注意static语句块:
static{
System.loadLibrary("hello");
}
在JVM载入HelloJNI类的时候加载动态库,System.loadLibrary()中的参数是我们要生成的动态库文件的名字,不包括扩展名,在Windows下面是hello.dll,Linux下面是hello.so,这个由Java自动识别。
2.编译此类
javacHelloJNI生成HelloJNI.class文件
3.使用javahHelloJNI生成C/C++头文件,生成的头文件不用任何修改。如下所示:
/*DONOTEDITTHISFILE-itismachinegenerated*/
#include<jni.h>
/*HeaderforclassHelloJNI*/
#ifndef_Included_HelloJNI
#define_Included_HelloJNI
#ifdef__cplusplus
extern"C"{
#endif
/*
*Class:HelloJNI
*Method:displayHello
*Signature:()V
*/
JNIEXPORTvoidJNICALLJava_HelloJNI_displayHello
(JNIEnv*,jobject);
/*
*Class:HelloJNI
*Method:showTime
*Signature:()V
*/
JNIEXPORTvoidJNICALLJava_HelloJNI_showTime
(JNIEnv*,jobject);
/*
*Class:HelloJNI
*Method:getLine
*Signature:(Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORTjstringJNICALLJava_HelloJNI_getLine
(JNIEnv*,jobject,jstring);
#ifdef__cplusplus
}
#endif
#endif
4.编写C/C++实现文件,如下所示:
//HelloJNIImpl.cpp--自己编写的实现文件
//2007-4-516:52:53
#include<jni.h>
#include<iostream>
#include<ctime>
#include<string>
#include<cstdio>
#include<windows.h>
#include"HelloJNI.h"
extern"C"{
//
}
usingnamespacestd;
JNIEXPORTvoidJNICALLJava_HelloJNI_displayHello(JNIEnv*env,jobjectobj){
cout<<"Hello,JNItech.ThisisfromC++!"<<endl;
}
JNIEXPORTvoidJNICALLJava_HelloJNI_showTime(JNIEnv*env,jobjectobj){
time_tsec;
tmt;
time_tloop;
cout<<"时间:";
sec=time(NULL);
t=*localtime(&sec);
unsignedinthour=t.tm_hour;
unsignedintmini=t.tm_min;
unsignedintsecd=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;
}
JNIEXPORTjstringJNICALLJava_HelloJNI_getLine(JNIEnv*env,jobjectobj,jstringprompt){
charbuf[128]={0};
constchar*str=(env)->GetStringUTFChars(prompt,0);
//printf("%s",str);
cout<<str;
(env)->ReleaseStringUTFChars(prompt,str);
stringbuffer;
getline(cin,buffer);
//scanf("%s",buf);
return(env)->NewStringUTF(buffer.c_str());
}
说明:JNITutorial中使用的居然是像下面这样的代码,env指针的使用应该有误,敬请注意。
JNIEXPORTjstringJNICALL
Java_Prompt_getLine(JNIEnv*env,jobjectobj,jstringprompt)
{
charbuf[128];
constchar*str=(*env)->GetStringUTFChars(env,prompt,0);
printf("%s",str);
(*env)->ReleaseStringUTFChars(env,prompt,str);
...
5.生成本地库文件。笔者使用的vs2008中的C++编译器。vs2008命令行工具生成DLL的命令:
cl-I<头文件目录1>-I<头文件目录2>-LD<C/C++源文件名>-Fe<生成的DLL文件名>
-I选项指定头文件目录,-LD指定C++源文件,-Fe指定生成的DLL文件名。将各个部分替换成实际的情况,实际使用的命令如下:
cl-C:\Java\jdk1.6.0_10\include-IC:\Java\jdk1.6.0_10\include\win32-LDHelloJNIImpl.cpp-Fehello.dll
关于这里的cl命令使用,及在vs2008中使用请看下一篇文章Vs2008与JNI示例。
6.运行结果,如下图所示:
7.自动化。其实步骤都很简单,所以可以使用Ant来完成。下面提供一个ANTBuildfile。使用之前请根据自己的实际情况修改相关属性。
注意:Builfile编码是UTF-8的。
build.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<projectdefault="help"basedir="."name="JNI_Hello">
<propertyname="app.home"value="."></property>
<!--设置头文件的目录,根据JDK具体的安装目录而定-->
<propertyname="h.dir1"value="C:/java/jdk1.5.0_10/include"></property>
<propertyname="h.dir2"value="C:/java/jdk1.5.0_10/include/win32"></property>
<!--设置cl工具的路径,视具体情况而定-->
<propertyname="vc.bin.dir"
value="C:/ProgramFiles/MicrosoftVisualStudio.NET2003/Vc7/bin">
</property>
<propertyname="cpp.name"value="HelloJNIImpl.cpp"></property>
<propertyname="dll.name"value="hello.dll"></property>
<targetname="compile"description="==>编译Java源文件">
<javacsrcdir="${app.home}"destdir="${app.home}">
</javac>
</target>
<targetname="javah"description="==>生成C/C++头文件">
<javahdestdir="${app.home}"force="yes"class="HelloJNI"/>
</target>
<targetname="dll"description="==>调用cl命令生成相关的DLL文件">
<execdir="${app.home}"executable="${vc.bin.dir}/cl.exe"os="WindowsXP">
<!--参数-EHsc好像是VS.NET2003中cl.exe的,可以不加,参考下面的注释行-->
<argline="-EHsc-I${h.dir1}-I${h.dir2}-LD${cpp.name}-Fe${dll.name}"/>
<!--
<argline="-I${h.dir1}-I${h.dir2}-LD${cpp.name}-Fe${dll.name}"/>
-->
</exec>
</target>
<targetname="run"description="==>运行">
<javaclassname="HelloJNI"></java>
</target>
<targetname="all"description="==>自动进行上面任务列表">
<antcalltarget="compile"/>
<antcalltarget="javah"/>
<antcalltarget="dll"/>
<antcalltarget="run"/>
</target>
<targetname="help"description="==>显示帮助信息">
<echo>用法帮助:</echo>
<echo>antcompile编译Java源文件</echo>
<echo>antjavah生成C/C++头文件</echo>
<echo>antdll调用cl命令生成相关的DLL文件</echo>
<echo>antrun运行</echo>
<echo></echo>
<echo>antall自动进行上面任务列表</echo>
<echo>anthelp显示帮助信息</echo>
</target>
</project>
Done!^_^!
JNI还有很多高级的功能,这里我就不说了,一来是不太会,二来也没什么意义。JNI Tutorial上讲得很全面,不错。