Sun-JNI文档系列之三——第二章:Getting Started

         本章我们将指导你完成一个使用JNI的简单实例。我们将写一个Java应用来调用C函数打印“Hello World!”

2.1 概述

         图2.1阐明了使用JDK或Java 2 SDK发行版来完成一个调用C函数打印“HelloWorld!”的Java程序的过程。整个过程包含下面几步:

         1、新建一个类文件(HelloWorld.java)用来声明本地方法

         2、使用Javac编译HelloWorld源文件,生成类文件HelloWorld.class。Javac编译器由JDK或Java 2 SDK发行版提供

         3、使用javah -jni命令生成C头文件(HelloWorld.h),在这个文件中包含了本地方法实现的函数原型。javah工具由JDK或Java 2 SDK发行版提供

         4、编写本地方法的C实现(HelloWorld.c)

         5、把C实现编译成本地库,生成HelloWorld.dll或libHelloWorld.so。这一步可以使用主机环境中可以使用的C编译器和链接器

         6、使用Java运行时解释器运行HelloWorld程序。类文件(HelloWorld.class)和本地库(HelloWorld.dll或libHelloWorld.so)在运行时都会被加载。


         本章的剩余部分将会详细讲解这些步骤。

2.2 声明本地方法

         让我们从下面的Java程序开始。这个程序定义了一个叫HelloWorld的类,在类中包含一个本地方法——print。

                   classHelloWorld {

                            privatenative void print();

                            publicstatic void main(String[] args) {

                            newHelloWorld().print();

                            }

                            static{

                            System.loadLibrary("HelloWorld");

                            }

                   }

         HelloWorld类的定义开始于本地方法print的声明。main方法紧随其后,main方法中实例化了HelloWorld类并为这个实例调用print方法。类定义的最后是一个静态的初始化程序(initializer),这个初始化程序负责加载包含着print方法的本地库。

         声明一个像print这样的本地方法与声明常规的Java方法有两点不同。本地方法声明必须包含native修饰符。native修饰符表示这个方法由其他语言实现。此外,本地方法由于在类自身中没有相关实现,因此声明以分号(the statement terminator symbol)结束。我们将在单独的C文件中实现print方法。

         在本地方法print被调用之前,实现了此方法的本地库必须被加载完毕。在这种情况下,我们在HelloWorld类的静态初始化程序中加载本地库。Java虚拟机先运行静态初始化程序再调用HelloWorld类中的方法。这样就保证了本地库在print被调用前被加载。

         我们定义一个main方法使之能够运行HelloWorld类。HelloWorld.main像调用普通方法那样调用本地方法print。

         System.loadLibrary获取本地库的名称,然后找到与名称相符的本地库并将本地库加载到应用中。我们将在本书的后续部分讨论详细的加载过程。现在你只需要简单地记住为了使System.loadLibrary("HelloWorld")成功执行,我们需要创建一个本地库,这个本地库在Win32平台上叫HelloWorld.dll,在Solaris中叫libHelloWorld.so。

2.3 编译HelloWorld类

         当你定义了HelloWorld类之后,把代码保存在一个叫HelloWorld.java的文件中。然后用JDK或Java 2 SDK发行版自带的javac编译器编译源码文件:

         javacHelloworld.java

         这个命令将在当前目录下生成HelloWorld.class。

2.4 创建本地方法头文件

         接下来我们将使用javah工具生成JNI风格的头文件,这个头文件在用C实现本地方法时十分有用。你可以像下面这样基于HelloWorld类运行javah:

         javah-jni HelloWorld

         头文件名是类的名称然后以“.h”结尾。上面展示的命令生成一个叫HelloWorld.h的文件。在这里我们不列举生成的头文件的全部内容。头文件中最重要的部分是Java_HelloWorld_print的函数原型,这是一个实现了HelloWorld.print的C函数:

         JNIEXPORTvoid JNICALL Java_HelloWorld_print (JNIEnv *, jobject);

         暂时先忽略JNIEXPORT和JNICALL这两个宏。你可能会发现,在本地方法的C语言实现中传入了两个参数而在对应的本地方法声明中没有传入任何参数。每一个本地方法实现的第一个参数都是JNIEnv接口指针。第二个参数是一个只想HelloWorld对象自身的引用(分类sort of 上比较像C++中的this指针)。我们将在本书的后续部分讨论JNIEnv接口指针和jobject参数,但是这个简单的例子中可以忽略这两个参数。

2.5 实现本地方法

         javah生成的JNI风格头文件帮助你用C/C++实现本地方法。你要实现的额函数必须遵循头文件中声明的函数原型。你可以在HelloWorld.c中像下面这样实现HelloWorld.print方法:

                   #include<jni.h>

                   #include<stdio.h>

                   #include"HelloWorld.h"

                   JNIEXPORTvoid JNICALL

                   Java_HelloWorld_print(JNIEnv*env, jobject obj)

                   {

                            printf("HelloWorld!\n");

                            return;

                   }

         本地方法的实现很简单,使用了printf函数打印字符串“Hello World!”然后return。正如之前提到过的,JNIEnv指针和对象自身的引用这两个参数都没有使用。

         C程序中vaohanle三个头文件:

         1、jni.h —— 这个头文件提供本地代码调用JNI函数所需要的信息。当实现本地方法时,在你的C/C++源文件中必须包含这个文件

         2、stdio.h —— 上面的代码段也包含了stdio.h,因为它使用了printf函数。

         3、HeloWorld.h —— 你使用javah生成的头文件。它包含了Java_HelloWorld_print函数的C/C++原型。

2.6 编译C源文件并生成本地库

         还记得你在HelloWorld.java中创建HelloWorld类时,你用一行代码把一个本地库加载到程序中吗:

                   System.loadLibrary("HelloWorld");

         现在所有必须的C代码已经完成了,下面你需要编译HelloWorld.c构建本地库。不同的操作系统构建本地库的方法不同。在Solaris上,使用下面的命令可以构建一个叫libHelloWorld.so的共享库:

                   cc-G -I /java/include -I /java/include/solaris HelloWorld.c -o libHelloWorld.so

         -G选项告诉C编译器生成一个共享库而不是生成常规的Solaris可执行文件。在本书中,由于纸张宽度的限制,我们把命令分到了两行中。你需要在一行中输入这个命令或者把命令放到脚本文件中。在Win32平台,下面的命令将使用微软VC++编译器构建动态链接库(DLL)(见注1

                   cl-I c:\java\include -I c:\java\include\win32 -MD -LD HelloWorld.c-FeHelloWorld.dll

         -MD选项确保HelloWorld.dll与Win32多线程C库链接。(The-MDoptionensures thatHelloWorld.dllis linked with the Win32 multithreaded C library.)-LD选项确保生成一个DLL而不是Win32可执行程序。当然,在Solaris和Win32中,你都需要加入你自己机器配置的包含路径。(you need to put in the include paths that reflect the setup on yourown machine.)

2.7 运行程序

         到这里你已经有了两个准备用来运行程序的组件。类文件(HelloWorld.class)调用本地方法,本地库(HelloWorld.dll)实现本地方法。

         由于HelloWorld类包含自己的main方法,因此你可以像下面这样在Solaris或Win32上运行程序(见注2

                   javaHelloWorld

         你会看到如下输出:

                   HelloWorld!

         正确设置你的本地库路径对于程序的运行是十分重要的。本地库路径是Java虚拟机在加载本地库时搜索的目录列表。如果你没有正确设置本地库路径,你将会看到类似下面的错误:

                   java.lang.UnsatisfiedLinkError:no HelloWorld in library path

                                     atjava.lang.Runtime.loadLibrary(Runtime.java)

                                     atjava.lang.System.loadLibrary(System.java)

                                     atHelloWorld.main(HelloWorld.java)

         确定使用的本地库位于本地库路径中某个目录下。如果你是在Solaris系统中运行程序,LD_LIBRARY_PATH环境变量被用来设置本地库路径。确定LD_LIBRARY_PATH包含libHelloWorld.so文件所在的目录。如果libHelloWorld.so文件位于当前目录下,你可以在sh或ksh中使用下面两条命令设置合适的LD_LIBRARY_PATH环境变量:

                   LD_LIBRARY_PATH=.exportLD_LIBRARY_PATH

         在C shell中等价的命令是:

                   setenvLD_LIBRARY_PATH .

         如果你在Windows95或者Windows NT上运行,确认HelloWorld.dll在当前目录下或者在PATH环境变量列表中的某一目录下

         在Java 2 SDK发行版中,你也可以像下面那样在Java命令行中把本地库路径作为系统特性(system property)指明:

                   java-D java.library.path=. HelloWorld

         “-D”选项设置sets aJava platform system property。把java.libiraries.path设置为“.”引导Java虚拟机在当前目录下搜索本地库。

 

1DLL文件生成时使用文档中的命令会出现如下错误:

        

尝试解决之后又引出一系列新的错误,最终解决方案是在VC中新建一个空的Win32 Dynamic-Link Library工程HelloWorld,然后手动创建HelloWorld.cHelloWorld.h,,并将相应代码复制到两个文件中。然后如果编译出错,将JDK中的jni.hjni_md.h拷到VCinclude文件夹下即可顺利生成HelloWorld.dll

 

2运行程序时,我是用的开发环境是win7 64位旗舰版,JDK版本是64位版jdk1.6.0_45,而编译生成的dll是32位版本,因此运行时会出现如下错误:


我不清楚在一台电脑上同时装32位版和64位版jdk会不会相互影响,因此我在xp SP3环境下装了相应版本的32位版jdk,下面是运行截图:


微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码 微信小程序毕业设计期末大作业项目源码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值