JNI入门教程之HelloWorld篇

资料引用:http://www.knowsky.com/363050.html

本文讲述如何使用JNI技术实现HelloWorld,目的是让读者熟悉JNI的机制并编写第一个HelloWorld程序。

JavaNativeInterfaceJNI)是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;

}

注重我们这里includejni.h和刚才得到的HelloWorld.h文件。因此你要在VC++里面设置好,jni.hJAVA_HOME/include里面。编译通过后再生成hello.dll文件。

运行java程序

把上面生成的hello.dll文件复制到我们的工作目录,这时候我们的目录中包括HelloWorld.javaHelloWorld.classhello.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);

这里JNIEXPORTJNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVAint类型与本地的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);
  这里JNIEXPORTJNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVAint类型与本地的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进行编译,操作比较简单!)这个头文件就是JavaC之间的纽带。要特别注意的是方法中传递的参数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中最麻烦的部分,也是最关键的部分,参数的传递。由于JavaC的编码是不同的,所以传递的参数是要进行再处理,否则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("

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值