资料引用:http://www.knowsky.com/363050.html
本文讲述如何使用JNI技术实现HelloWorld,目的是让读者熟悉JNI的机制并编写第一个HelloWorld程序。
JavaNativeInterface(JNI)是Java语言的本地编程接口,是J2SDK的一部分。在java程序中,我们可以通过JNI实现一些用java语言不便实现的功能。通常有以下几种情况我们需要使用JNI来实现。
标准的java类库没有提供你的应用程序所需要的功能,通常这些功能是平台相关的
你希望使用一些已经有的类库或者应用程序,而他们并非用java语言编写的
程序的某些部分对速度要求比较苛刻,你选择用汇编或者c语言来实现并在java语言中调用他们
在《java核心技术》中,作者提到JNI的时候,建议不到万不得已不要使用JNI技术,一方面它需要你把握更多的知识才可以驾驭,一方面使用了JNI你的程序就会丧失可移植性。在本文我们跳过JNI的底层机制,读者最好先把它想象为本地代码和java代码的粘合剂。关系如下图所示:
下面我们开始编写HelloWorld程序,由于涉及到要编写c/c++代码因此我们会在开发中使用MicrosoftVC++工具。
编写java代码
我们在硬盘上建立一个hello目录作为我们的工作目录,首先我们需要编写自己的java代码,在java代码中我们会声明native方法,代码非常简单。如下所示
classHelloWorld
{
publicnativevoiddisplayHelloWorld();
static{
System.loadLibrary("hello");
}
publicstaticvoidmain(String[]args){
newHelloWorld().displayHelloWorld();
}
}
注重我们的displayHelloWorld()方法的声明,它有一个要害字native,表明这个方法使用java以外的语言实现。方法不包括实现,因为我们要用c/c++语言实现它。注重System.loadLibrary("hello")这句代码,它是在静态初始化块中定义的,系统用来装载hello共享库,这就是我们在后面生成的hello.dll(假如在其他的操作系统可能是其他的形式,比如hello.so)
编译java代码
javacHelloWorld.java生成HelloWorld.class文件
创建.h文件
这一步中我们要使用javah命令生成.h文件,这个文件要在后面的c/c++代码中用到,我们运行
javahHelloWorld。这样我们可以看到在相同目录下生成了一个HelloWorld.h文件,文件内容如下
在此我们不对他进行太多的解释。
/*DONOTEDITTHISFILE-itismachinegenerated*/
#include<jni.h>
/*HeaderforclassHelloWorld*/
#ifndef_Included_HelloWorld
#define_Included_HelloWorld
#ifdef__cplusplus
extern"C"{
#endif
/*
*Class:HelloWorld
*Method:displayHelloWorld
*Signature:()V
*/
JNIEXPORTvoidJNICALLJava_HelloWorld_displayHelloWorld
(JNIEnv*,jobject);
#ifdef__cplusplus
}
#endif
#endif
编写本地实现代码
在这部分我们要用C/C++语言实现java中定义的方法,我们在VC++中新建一个Project,然后创建一个HelloWorldImp.cpp文件,内容如下
#include<jni.h>
#include"HelloWorld.h"
#include<stdio.h>
JNIEXPORTvoidJNICALL
Java_HelloWorld_displayHelloWorld(JNIEnv*env,jobjectobj)
{
printf("Helloworld!\n");
return;
}
注重我们这里include了jni.h和刚才得到的HelloWorld.h文件。因此你要在VC++里面设置好,jni.h在JAVA_HOME/include里面。编译通过后再生成hello.dll文件。
运行java程序
把上面生成的hello.dll文件复制到我们的工作目录,这时候我们的目录中包括HelloWorld.java,HelloWorld.class和hello.dll文件。运行javaHelloWorld命令,则可在控制台看到Helloworld
的输出了。
-
资料引用:http://www.knowsky.com/363050.html
Java:JNI完全手册时间:2007-02-1000:00:00来源:作者:
最近在公司里做了一个手机的项目,需要JAVA程序在发送短信的时候和第三方的短信服务器连接。短信接口是用C++写的。琢磨了三天,大致搞懂了JNI的主体部分。先将心得整理,希望各位朋友少走弯路。
首先引用一篇文章,介绍一个简单的JNI的调用的过程。
JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。
JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。
简单介绍及应用如下:
一、JAVA中所需要做的工作
在JAVA程序中,首先需要在类中声明所调用的库名称,如下:
static{
System.loadLibrary(“goodluck”);
}
在这里,库的扩展名字可以不用写出来,究竟是DLL还是SO,由系统自己判断。
还需对将要调用的方法做本地声明,关键字为native。且只需要声明,而不需要具体实现。如下:
publicnativestaticvoidset(inti);
publicnativestaticintget();
然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。
例如程序testdll.java,内容为:
publicclasstestdll
{
static
{
System.loadLibrary(\"goodluck\");
}
publicnativestaticintget();
publicnativestaticvoidset(inti);
publicstaticvoidmain(String[]args)
{
testdlltest=newtestdll();
test.set(10);
System.out.println(test.get());
}
}
用javactestdll.java编译它,会生成testdll.class。
再用javahtestdll,则会在当前目录下生成testdll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。
二、C/C++中所需要做的工作
对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。
接上例子。我们先看一下testdll.h文件的内容:
/*DONOTEDITTHISFILE-itismachinegenerated*/
#include
/*Headerforclasstestdll*/
#ifndef_Included_testdll
#define_Included_testdll
#ifdef__cplusplus
extern\"C\"{
#endif
/*
*Class:testdll
*Method:get
*Signature:()I
*/
JNIEXPORTjintJNICALLJava_testdll_get(JNIEnv*,jclass);
/*
*Class:testdll
*Method:set
*Signature:(I)V
*/
JNIEXPORTvoidJNICALLJava_testdll_set(JNIEnv*,jclass,jint);
#ifdef__cplusplus
}
#endif
#endif
在具体实现的时候,我们只关心两个函数原型
JNIEXPORTjintJNICALLJava_testdll_get(JNIEnv*,jclass);和
JNIEXPORTvoidJNICALLJava_testdll_set(JNIEnv*,jclass,jint);
这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVA的int类型与本地的int沟通的一种类型,我们可以视而不见,就当做int使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。
好,下面我们用testdll.cpp文件具体实现这两个函数:
#include\"testdll.h\"
inti=0;
JNIEXPORTjintJNICALLJava_testdll_get(JNIEnv*,jclass)
{
returni;
}
JNIEXPORTvoidJNICALLJava_testdll_set(JNIEnv*,jclass,jintj)
{
i=j;
}
编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是goodluck.dll。把goodluck.dll拷贝到testdll.class的目录下,javatestdll运行它,就可以观察到结果了。
我的项目比较复杂,需要调用动态链接库,这样在JNI传送参数到C程序时,需要对参数进行处理转换。才可以被C程序识别。
大体程序如下:
publicclassSendSMS{
static
{
System.out.println(System.getProperty(\"java.library.path\"));
System.loadLibrary(\"sms\");
}
publicnativestaticintSmsInit();
publicnativestaticintSmsSend(byte[]mobileNo,byte[]smContent);
}
在这里要注意的是,path里一定要包含类库的路径,否则在程序运行时会抛出异常:
java.lang.UnsatisfiedLinkError:nosmsinjava.library.path
atjava.lang.ClassLoader.loadLibrary(ClassLoader.java:1491)
atjava.lang.Runtime.loadLibrary0(Runtime.java:788)
atjava.lang.System.loadLibrary(System.java:834)
atcom.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
atcom.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
Exceptioninthread\"main\"
指引的路径应该到.dll文件的上一级,如果指到.dll,则会报:
java.lang.UnsatisfiedLinkError:C:\sms.dll:Can'tfinddependentlibraries
atjava.lang.ClassLoader$NativeLibrary.load(NativeMethod)
atjava.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560)
atjava.lang.ClassLoader.loadLibrary(ClassLoader.java:1485)
atjava.lang.Runtime.loadLibrary0(Runtime.java:788)
本篇教程来源于完全教程网原文链接:http://www.pcstu.com/program/Java/jc/20070210/19659.html
JNI完全手册
作者:dankes…日期:2005-11-1510:49:09来源:不详点击:次评论
最近在公司里做了一个手机的项目,需要JAVA程序在发送短信的时候和第三方的短信服务器连接。短信接口是用C++写的。琢磨了三天,大致搞懂了JNI的主体部分。先将心得整理,希望各位朋友少走弯路。
首先引用一篇文章,介绍一个简单的JNI的调用的过程。
JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。
JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。
简单介绍及应用如下:
一、JAVA中所需要做的工作
在JAVA程序中,首先需要在类中声明所调用的库名称,如下:
static{
System.loadLibrary(“goodluck”);
}
在这里,库的扩展名字可以不用写出来,究竟是DLL还是SO,由系统自己判断。
还需要对将要调用的方法做本地声明,关键字为native。并且只需要声明,而不需要具体实现。如下:
publicnativestaticvoidset(inti);
publicnativestaticintget();
然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。
例如程序testdll.java,内容为:
publicclasstestdll
{
static
{
System.loadLibrary("goodluck");
}
publicnativestaticintget();
publicnativestaticvoidset(inti);
publicstaticvoidmain(String[]args)
{
testdlltest=newtestdll();
test.set(10);
System.out.println(test.get());
}
}
用javactestdll.java编译它,会生成testdll.class。
再用javahtestdll,则会在当前目录下生成testdll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。
二、C/C++中所需要做的工作
对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。
接上例子。我们先看一下testdll.h文件的内容:
/*DONOTEDITTHISFILE-itismachinegenerated*/
#include
/*Headerforclasstestdll*/
#ifndef_Included_testdll
#define_Included_testdll
#ifdef__cplusplus
extern"C"{
#endif
/*
*Class:testdll
*Method:get
*Signature:()I
*/
JNIEXPORTjintJNICALLJava_testdll_get(JNIEnv*,jclass);
/*
*Class:testdll
*Method:set
*Signature:(I)V
*/
JNIEXPORTvoidJNICALLJava_testdll_set(JNIEnv*,jclass,jint);
#ifdef__cplusplus
}
#endif
#endif
在具体实现的时候,我们只关心两个函数原型
JNIEXPORTjintJNICALLJava_testdll_get(JNIEnv*,jclass);和
JNIEXPORTvoidJNICALLJava_testdll_set(JNIEnv*,jclass,jint);
这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVA的int类型与本地的int沟通的一种类型,我们可以视而不见,就当做int使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。
好,下面我们用testdll.cpp文件具体实现这两个函数:
#include"testdll.h"
inti=0;
JNIEXPORTjintJNICALLJava_testdll_get(JNIEnv*,jclass)
{
returni;
}
JNIEXPORTvoidJNICALLJava_testdll_set(JNIEnv*,jclass,jintj)
{
i=j;
}
编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是goodluck.dll。把goodluck.dll拷贝到testdll.class的目录下,javatestdll运行它,就可以观察到结果了。
我的项目比较复杂,需要调用动态链接库,这样在JNI传送参数到C程序时,需要对参数进行处理转换。才可以被C程序识别。
大体程序如下:
publicclassSendSMS{
static
{
System.out.println(System.getProperty("java.library.path"));
System.loadLibrary("sms");
}
publicnativestaticintSmsInit();
publicnativestaticintSmsSend(byte[]mobileNo,byte[]smContent);
}
在这里要注意的是,path里一定要包含类库的路径,否则在程序运行时会抛出异常:
java.lang.UnsatisfiedLinkError:nosmsinjava.library.path
atjava.lang.ClassLoader.loadLibrary(ClassLoader.java:1491)
atjava.lang.Runtime.loadLibrary0(Runtime.java:788)
atjava.lang.System.loadLibrary(System.java:834)
atcom.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
atcom.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
Exceptioninthread"main"
指引的路径应该到.dll文件的上一级,如果指到.dll,则会报:
java.lang.UnsatisfiedLinkError:C:\sms.dll:Can'tfinddependentlibraries
atjava.lang.ClassLoader$NativeLibrary.load(NativeMethod)
atjava.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560)
atjava.lang.ClassLoader.loadLibrary(ClassLoader.java:1485)
atjava.lang.Runtime.loadLibrary0(Runtime.java:788)
atjava.lang.System.loadLibrary(System.java:834)
atcom.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
atcom.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
Exceptioninthread"main"
通过编译,生成com_mobilesoft_sms_mobilesoftinfo_SendSMS.h头文件。(建议使用Jbuilder进行编译,操作比较简单!)这个头文件就是Java和C之间的纽带。要特别注意的是方法中传递的参数jbyteArray,这在接下来的过程中会重点介绍。
/*DONOTEDITTHISFILE-itismachinegenerated*/
#include
/*Headerforclasscom_mobilesoft_sms_mobilesoftinfo_SendSMS*/
#ifndef_Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS
#define_Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS
#ifdef__cplusplus
extern"C"{
#endif
/*
*Class:com_mobilesoft_sms_mobilesoftinfo_SendSMS
*Method:SmsInit
*Signature:()I
*/
JNIEXPORTjintJNICALLJava_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit
(JNIEnv*,jclass);
/*
*Class:com_mobilesoft_sms_mobilesoftinfo_SendSMS
*Method:SmsSend
*Signature:([B[B)I
*/
JNIEXPORTjintJNICALLJava_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend
(JNIEnv*,jclass,jbyteArray,jbyteArray);
#ifdef__cplusplus
}
#endif
#endif
对于我要调用的C程序的动态链接库,C程序也要提供一个头文件,sms.h。这个文件将要调用的方法罗列了出来。
/*
*SMSAPI
*Author:yippit
*Date:2004.6.8
*/
#ifndefMCS_SMS_H
#defineMCS_SMS_H
#defineDLLEXPORT__declspec(dllexport)
/*smsstorage*/
#defineSMS_SIM0
#defineSMS_MT1
/*smsstates*/
#defineSMS_UNREAD0
#defineSMS_READ1
/*smstype*/
#defineSMS_NOPARSE-1
#defineSMS_NORMAL0
#defineSMS_FLASH1
#defineSMS_MMSNOTI2
typedefstructtagSmsEntry{
intindex;/*index,startfrom1*/
intstatus;/*read,unread*/
inttype;/*-1-can'tparser0-normal,1-flash,2-mms*/
intstorage;/*SMS_SIM,SMS_MT*/
chardate[24];
charnumber[32];
chartext[144];
}SmsEntry;
DLLEXPORTintSmsInit(void);
DLLEXPORTintSmsSend(char*phonenum,char*content);
DLLEXPORTintSmsSetSCA(char*sca);
DLLEXPORTintSmsGetSCA(char*sca);
DLLEXPORTintSmsSetInd(intind);
DLLEXPORTintSmsGetInd(void);
DLLEXPORTintSmsGetInfo(intstorage,int*max,int*used);
DLLEXPORTintSmsSaveFlash(intflag);
DLLEXPORTintSmsRead(SmsEntry*entry,intstorage,intindex);
DLLEXPORTintSmsDelete(intstorage,intindex);
DLLEXPORTintSmsModifyStatus(intstorage,intindex);/*unread->read*/
#endif
在有了这两个头文件之后,就可以进行C程序的编写了。也就是实现对JNI调用的两个方法。在网上的资料中,由于调用的方法实现的都比较简单,(大多是打印字符串等)所以避开了JNI中最麻烦的部分,也是最关键的部分,参数的传递。由于Java和C的编码是不同的,所以传递的参数是要进行再处理,否则C程序是会对参数在编译过程中提出警告,例如;warningC4024:'SmsSend':differenttypesforformalandactualparameter2等。
Sms.c的程序如下:
#include"sms.h"
#include"com_mobilesoft_sms_mobilesoftinfo_SendSMS.h"
JNIEXPORTjintJNICALLJava_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit(JNIEnv*env,jclassjobject)
{
returnSmsInit();
}
JNIEXPORTjintJNICALLJava_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend(JNIEnv*env,jclassjobject,jbyteArraymobileno,jbyteArraysmscontent)
{
char*pSmscontent;
//jsizetheArrayLengthJ=(*env)->GetArrayLength(env,mobileno);
jbyte*arrayBody=(*env)->GetByteArrayElements(env,mobileno,0);
char*pMobileNo=(char*)arrayBody;
printf("[%s]\n",pMobileNo);
//jsizesize=(*env)->GetArrayLength(env,smscontent);
arrayBody=(*env)->GetByteArrayElements(env,smscontent,0);
pSmscontent=(char*)arrayBody;
printf("