自己实现一个Native方法的调用
JNI
开始本篇的内容之前,首先要讲一下JNI。Java很好,使用的人很多、应用极广,但是Java不是完美的。Java的不足体现在运行速度要比传统的C++慢上许多之外,还有Java无法直接访问到操作系统底层如硬件系统,为此Java提供了JNI来实现对于底层的访问。JNI,Java Native Interface,它是Java的SDK一部分,JNI允许Java代码使用以其他语言编写的代码和代码库,本地程序中的函数也可以调用Java层的函数,即JNI实现了Java和本地代码间的双向交互。
Native
JDK开放给用户的源码中随处可见Native方法,被Native关键字声明的方法说明该方法不是以Java语言实现的,而是以本地语言实现的,Java可以直接拿来用。这里有一个概念,就是本地语言,本地语言这四个字,个人理解应该就是可以和操作系统直接交互的语言。
通过JNI调用C++写的代码
下面演示一下如何写一段简单的C++代码,在Java中用一个Native方法去调用的步骤。C++代码使用到的IDE是Microsoft Visual Studio 2010,这是一款市面上开发C++代码最常用的IDE,功能强大。OK,接下来一步一步演示一下:
1、写一段Java代码。由于我们在Windows环境下,所以用的是一个.dll文件,如果在Linux环境下的话,用的是一个.so文件。最后C++代码写完之后要生成一个.dll/.so文件,生成的文件可以使用static静态加载的方法加载进来,也可以通过配置环境变量的方式,这里选择前者。
1 public class TestMain 2 { 3 static 4 { 5 System.load("D:" + File.separator + "Hello.dll"); 6 } 7 8 public native static void Hello(); 9 10 public static void main(String[] args) 11 { 12 Hello(); 13 } 14 }
2、cmd进入命令行程序中,进入刚才写的那个类的CLASSPATH下,CLASSPATH就是.class文件所在的根路径。运行“JNI -jni com.xrq.test1.TestMain” ,表示为指定的类下的Native方法生成.h文件。.h文件是C/C++使用的头文件,用于将声明和实现分离,不熟悉C/C++的同学也不用深究
3、进入自己的CLASSPATH底下查看一下有没有多一个.h文件,我自己这边的是
这个命名是javah这个命令的实现帮我们命名的,只要多了这个文件就可以了
4、打开VS2010,文件-->新建-->项目,命名为“Hello”,和我们静态块中load进去的名字要一致
注意选择DLL
5、刚才通过javah生成的.h文件复制到Hello目录下
6、把这个.h添加到项目中,右键头文件-->添加-->现有项,选择Hello目录下的“com_xrq_test1_TestMain.h”添加进去就好了
7、右键源文件-->添加-->新建项,选择.cpp文件,随便命名,我叫做source.cpp。实现很简单,就打印一下“Hello”这个字符串。cout是C++的控制台输出语句,相当于Java的System.out.print(),endl是换行的意思,所以这整句相当于Java的System.out.println("Hello");
8、修改“com_xrq_test1_TestMain.h”头文件中的#include <jni.h>为#include "jni.h"
9、把%JAVA_HOME%/include目录下的“jni.h”和%JAVA_HOME%/include/win32目录下的“jni_md.h”两个文件复制到Hello目录下,用刚才添加“com_xrq_test1_TestMain.h”一样的方式添加这两个.h文件到头文件目录中,这样目录下Hello目录下应该多出了两个文件,VS2010工程目录下应该有3个.h文件。JAVA_HOME就是JDK安装目录,不知道的cmd进入命令行,echo %JAVA_HOME%就可以查看了。这一步很重要,没有这一步,C++程序是无法运行的
10、右键项目-->生成,就可以生成.dll文件了
11、刚才生成的.dll文件是32系统下的,32系统的用户可以直接使用这个.dll文件了,只要复制到静态代码块指定的目录下就好了。换句话说,64位系统的用户是不能使用这个.dll文件的。如果要生成64位计算机可以使用的.dll文件,还要额外再为64位计算机成生一个.dll文件
下拉菜单里面没有x64的点击下拉菜单-->配置管理器配置一下,自己摸索一下就好了,VS2010有自带x64的
12、生成64位的.dll,复制到D盘下即可,也就是静态代码块里面指定的Hello.dll的路径
13、运行一下第一步里面写的Java程序,C++打印的“Hello”就出来了,至此,Java通过JNI调用C++编写的代码的功能完成。
有什么心得?
自己完成了这么一个过程,肯定是颇有成就感的,成就感过后,我们可以从这13个步骤中感悟到什么?至少个人有以下心得:
1、1个类中有很多Native方法-->这个类中的所有Native方法生成到1个.h文件中-->本地代码生成一个.dll/.so文件和一个类的Native方法实现相对应
2、为什么有Native方法的类中必有这么一段代码
1 private static native void (); 2 static { 3 registerNatives(); 4 }
现在想来,估计和我们的静态代码块起的作用一样,都是为这个类导入特定的.dll/.so文件用的。至于为什么不能像我们这么写,个人猜测,是因为不同的用户磁盘上的.dll/.so文件位置不固定,和JDK安装目录相关?
3、Java不在乎Native方法是用什么语言实现的,只要一来语言能和底层打交道就好了,二来语言实现完可以提供出来.dll/.so文件。因此同一个Native方法,如果不同的Java虚拟机去调用它,那么结果可能都不同,比如Object的hashCode(),当然,运行效率也不尽然相同,因为不同的虚拟机对于不同的Native方法有自己的实现。
我不能保证写的每个地方都是对的,但是至少能保证不复制、不黏贴,保证每一句话、每一行代码都经过了认真的推敲、仔细的斟酌。每一篇文章的背后,希望都能看到自己对于技术、对于生活的态度。
我相信乔布斯说的,只有那些疯狂到认为自己可以改变世界的人才能真正地改变世界。面对压力,我可以挑灯夜战、不眠不休;面对困难,我愿意迎难而上、永不退缩。
其实我想说的是,我只是一个程序员,这就是我现在纯粹人生的全部。
==================================================================================