`
libaohan
  • 浏览: 44938 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论
阅读更多
[原创]JNI完整的可行实例(绝对适合初学者)
踩踩 0作者:nicegiving 发表日期:2009-09-12 复制链接 收藏
最近需要用到些JNI的东西,在网上找了找资料,感觉说的不是很详细,初学者容易误入歧途,于是决定自己写一个教程,一是为了使自己更加熟悉JNI的基本操作流程,二是为了给广大初学者服务。

本文的七个大标题就是我总结出来的JNI的基本流程,在JNI中,本地化的方法是通过库文件为Java程序使用的,因此我们不但要编写Java程序,而且还需要有包含有本地化方法的库文件,这需要我们自己编写C++代码来编译生成。然后将生成的库文件拷贝到java的class文件或者jar路径中,然后运行class文件或者jar文件,这些库文件就会被自动的加载然后为Java程序服务。

在本文中,我们以一个例子贯穿其中,演示基本的JNI操作。
一、编写Java类
/**
??* File name: Testdll.java
??* JNI 演示基本程序
??* @author Zengming Zhang
??*/
public class Testdll {
??
??/*
?? * 首先要加载本地系统的库文件,windows下面是dll文件,Linux/Unix下面是so文件,
?? * 不需要给出扩展名,系统会为我们决定使用什么扩展名的文件。
?? */
??static{
????System.out.println("Loading library ...");
????System.loadLibrary("zzmdll"); // 加载本地的zzmdll.dll库文件
????System.out.println("Loading library completed");
??}
??
??/*
?? *?? 定义一个本地化的get函数和一个本地化的set函数,
?? *?? 在这里只需要声明就好,不需要实现,因为实现是在C++中完成的。
?? *?? 注意函数声明中的native关键字,这说明定义的方法是和本地系统密切相关的。
?? */
??public native static int get();
??public native static void set(int i);
??
??/*
?? * 测试函数
?? */
??public static void main(String args[]){
????Testdll test = new Testdll();
????test.set(199);
????System.out.println(test.get());
??}
}

二、编译Java类
我们使用javac命令来编译java文件,如下所示:
>javac Testdll.java
然后会生成Testdll.class文件,这个时候你如果运行这个文件的话肯定会报错的,因为java程序找不到要加载的类。

三、使用javah生成类库的头文件
对于为什么要这样做是很容易理解的,我们在java类中定义了自己的本地化方法的声明,但是这只是在java文件里面声明,但是实现却是要在C++程序里面,而在C++程序里面我们并不知道需要实现的函数原型,所以,我们需要一个头文件来为我们定义C++程序可以理解的函数原型,一般来说,这种东西都是存放在.h文件里面,而我们使用javah命令就是生成这种.h文件。
具体的命令执行如下所示:
>javah Testdll
然后我们就会在Testdll.class同目录下面发现生成的.h文件,文件名是Testdll.h,下面是该文件的内容:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Testdll */
#ifndef _Included_Testdll
#define _Included_Testdll
#ifdef __cplusplus
extern "C" {
#endif
/*
??* Class:???????? Testdll
??* Method:?????? get
??* Signature: ()I
??*/
JNIEXPORT jint JNICALL Java_Testdll_get
?? (JNIEnv *, jclass);
/*
??* Class:???????? Testdll
??* Method:?????? set
??* Signature: (I)V
??*/
JNIEXPORT void JNICALL Java_Testdll_set
?? (JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif

你会发现这个文件里面有很多新的名词,比如JNIEXPORT、jint等,JNIEXPORT是说这些函数是要被java调用的,jint是为了使java中的int和本地系统的int相互理解的中间体,我们就可以理解成int,你肯定也会猜到还有很多这样的定义,没错,这些都是JNI特有的,这些东西的定义在jni.h文件中(文件在JDK目录下面的include文件夹中)。

函数的声明方式,特别是函数的名字也是很有意思的,我们在java程序中的函数名字是get和set,而在这个头文件当中,函数的名字由三部分组成,Java打头的部分,说明是java调用的函数,然后是java类的class path路径,最后是java程序中定义的函数名,三部分使用下划线连接。

其他的没有什么好说的了。

四、编写对应的头文件的实现
这个部分就是本地化的C++开发了,我们要编写对应的函数的本地化实现,然后编译生成dll文件,这部分我使用VS2008来实现。

第一步,建立dll项目: 文件->新建->项目,在左边的Visual C++下面选择Win32,然后在右边的模板那里选择Win32项目,之后输入项目的名称,选择路径,点击确定按钮。在之后的Win32应用程序向导中,点击下一步,选择dll,其他的都不要勾选(当然默认是勾选预编译头的),点击完成按钮。

第二步,建立头文件:我们需要将用javah生成的Testdll.h文件弄过来,你可以新建一个,或者添加现有项。

第三步,编写实现文件(源文件):新建一个源文件,文件名是Testdll.cpp,该文件的具体内容如下:
#include "stdafx.h"
int i = 0;
JNIEXPORT jint JNICALL Java_Testdll_get
?? (JNIEnv *, jclass){
??return i;
}
JNIEXPORT void JNICALL Java_Testdll_set
?? (JNIEnv *, jclass, jint j){
??i = j;
}

第四步,修改stdafx.h文件:添加我们需要引用的头文件,即jni.h和Testdll.h两个文件,添加后的该文件内容如下:
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN???????????????????????? // 从 Windows 头中排除极少使用的资料
// Windows 头文件:
#include <windows.h>
// TODO: 在此处引用程序需要的其他头文件
#include <jni.h>
#include "Testdll.h"

五、编译生成*.dll文件(需要配置jni.h的路径,不然编译会出错)
由于VS2008默认是没有包含jni.h文件的,所以我们需要先设置下,不然编译会出错,设置方法如下,将以下文件:
\jdk\include\jni.h
\jdk\include\win32\jawt_md.h
\jdk\include\win32\jni_md.h
复制到VS2008安装目录下的\VC\include目录中即可。

然后右击工程,选择生成,如果没有错误的话,我们会在工程目录下面的debug路径下面找到编译生成的dll文件。

六、拷贝生成的*.dll文件到Java类的class文件路径下
将在第五步中生成的dll文件拷贝到java的.class或者jar路径下面即可,确保他们在同一目录中。

七、运行class文件
现在我们运行Testdll.class文件就不会出现错误了,具体的运行情况如下:
D:\Projects\Java-projects\workspace-4\JNI-example\bin>java Testdll
Loading library ...
Loading library completed
199

如果您有任何问题请联系我:nicegiving@gmail.com,欢迎学习交流!
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics