Chinaunix首页 | 论坛 | 博客
  • 博客访问: 497513
  • 博文数量: 135
  • 博客积分: 3010
  • 博客等级: 中校
  • 技术积分: 905
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-24 19:31
文章分类

全部博文(135)

文章存档

2010年(135)

我的朋友

分类: LINUX

2010-02-06 22:29:38

JNI简介及示例
2008-01-22 04:44 P.M.
一、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[] argsthrows 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,第二个使用头文件中的相关函数计算当前时间,第三个读取输入。注意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
/* 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
#include
#include
#include
#include
#include
#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 -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

"1.0" encoding="UTF-8"?>
default="help" basedir="." name="JNI_Hello">
  "app.home" value=".">

  
  "h.dir1" value="C:/java/jdk1.5.0_10/include">
  "h.dir2" value="C:/java/jdk1.5.0_10/include/win32">
  
  
  "vc.bin.dir" 
    value="C:/Program Files/Microsoft Visual Studio .NET 2003/Vc7/bin">
  


  "cpp.name" value="HelloJNIImpl.cpp">
  "dll.name" value="hello.dll">

  "compile" description=" ==> 编译Java源文件">
    "${app.home}" destdir="${app.home}">
    

  


  "javah" description=" ==> 生成C/C++头文件">
    "${app.home}" force="yes" class="HelloJNI"/>
  

  
  "dll" description=" ==> 调用cl命令生成相关的DLL文件">
    "${app.home}" executable="${vc.bin.dir}/cl.exe" os="Windows XP">
      
      "-EHsc -I${h.dir1} -I${h.dir2} -LD ${cpp.name} -Fe${dll.name}"/>
      
    

  


  "run" description=" ==> 运行">
    "HelloJNI">
  


  "all" description=" ==> 自动进行上面任务列表">
    "compile"/>
    "javah"/>
    "dll"/>
    "run"/>
  


  "help" description=" ==> 显示帮助信息">
    用法帮助:
    ant compile      编译Java源文件
    ant javah        生成C/C++头文件
    ant dll          调用cl命令生成相关的DLL文件
    ant run          运行
    
    ant all          自动进行上面任务列表
    ant help         显示帮助信息
  



Done!^_^!

JNI还有很多高级的功能,这里我就不说了,一来是不太会,二来也没什么意义。JNI Tutorial上讲得很全面,不错。

阅读(1292) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~