写一篇大家一看就会的教程《JNI初步》(转)

JNI初步。让大家一看就会哈哈。

写篇教程吧,网上很多关于JNIhello world,虽然称作哈喽沃德,但是调试起来还是不可避免遇到很多问题。今天在调式成功的这个里程碑时间里,总结下这两天俺的经验,那些希望使用JNI的银们,如果看到这篇小文了,或许可以一站式解决问题,免去东奔西走的麻烦咧~~~~哈哈哈

开始!

JNI是啥就不介绍了,最直观的作用就是它可以在java里面调用dll。如果大家伙儿遇到c++写的代码想转向java使用,不妨生成一个dll,再按下面的步骤来。

1.       编写java代码。文件名:HelloWorld.java

代码如下:

public class HelloWorld

{

static

{

System.loadLibrary("HelloWorldDll");

}

public native static int MyMethod();

 

public static void main(String[] args)

{

HelloWorld hw = new HelloWorld();

hw.MyMethod();

}

}

2.       编译java代码,生成class文件。

在命令行下输入:javac HelloWorld.java

完成后,D盘根目录下产生了HelloWorld.class文件。

PS:如果这里出现问题没有成功,那请检查你的环境变量是否设置正确。以及确认命令提示符是在当前你要编译的java文件的位置。以下是错误举例:

问题1

 

 

出错原因:你肯定粗心了,在代码中使用的类名和文件名不一致。图中所示错误是将类名写成了HelloWorlds

3.       根据class文件生成HelloWorld.h,以供C++调用。

在命令行下输入:javah HelloWorld

完成后,D盘根目录下产生了HelloWorld.h文件。

生成的代码如下:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class HelloWorld */

 

#ifndef _Included_HelloWorld

#define _Included_HelloWorld

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     HelloWorld

 * Method:    MyMethod

 * Signature: ()I

 */

JNIEXPORT jint JNICALL Java_HelloWorld_MyMethod

  (JNIEnv *, jclass);

 

#ifdef __cplusplus

}

#endif

#endif

这里需要做一点改动,将第二行的#include <jni.h>改成#include “jni.h”。否则vc生成dll编译的时候可能产生问题。

以下是这一步骤的错误举例:

问题一:

出错原因:编写的java程序中出现了包的说明:package com.microsoft.services;将包去掉就中了。关于带包编译的情况,下面分解。

4.       使用VC++创建dll程序。文件名:HelloWorldDll.dll

4.1新建一个名叫HelloWorldDlldll工程:

4.2HelloWorldDll.cpp文件头部加入:#include “HelloWorldDll.h”

4.3HelloWorldDll.cpp文件中再加入:

JNIEXPORT jint JNICALL Java_HelloWorld_MyMethod (JNIEnv *, jclass)

{

printf("this is helloworld printed in dll");

return 1;

}

代码中的jint是指名java中的返回值类型为int型。Java_后面跟上java文件的类名,然后再用一个_跟上方法名。括号中的参数不必做改动。

这里需要注意的有两点:

第一,   如果类带有包。那么Java_HelloWorld_MyMethod的第一个_前面需要把包名加上,改成Java_<包名>_HelloWorld_MyMethod。详细示例一会儿继续往下看。

第二,   第二,如果希望传入参数,那么在后面继续加就是了,例如,我想传入int型的变量a,那么第一行需要改成:

JNIEXPORT jint JNICALL Java_HelloWorld_MyMethod (JNIEnv *, jclass,jint a)

函数体内a就可以拿来随意整了。

编译生成dll文件吧。

出错举例:

错误1

C:\work\VCProjects\HelloWorldDll\HelloWorldDll.cpp(6) : fatal error C1083: Cannot open include file: 'HelloWorld.h': No such file or directory

原因:没有把D盘下的HelloWorld.h拷贝到工程目录下面。

错误2

c:\work\vcprojects\helloworlddll\helloworld.h(2) : fatal error C1083: Cannot open include file: 'jni.h': No such file or directory

原因:没有把jni.h拷贝到工程目录下。Jin.h的位置:

C:\Program Files\Java\jdk1.5.0_06\include

错误3

c:\work\vcprojects\helloworlddll\helloworld.h(2) : fatal error C1083: Cannot open include file: 'jni.h': No such file or directory

原因:呵呵为什么我已经把jni.h拷贝到工程目录下了,还是会报这个错误呢?那是因为HelloWorld.h里面的#include <jni.h>忘了改成#include “jni.h”

错误4

c:\work\vcprojects\helloworlddll\jni.h(27) : fatal error C1083: Cannot open include file: 'jni_md.h': No such file or directory

原因:又是找不到.h。这个jni_md.h藏在

C:\Program Files\Java\jdk1.5.0_06\include\win32下面。

5.       运行看效果。

将刚才生成的HelloWorldDll.dll拷贝到D盘根目录下,从命令行执行:

Java HelloWorld

可以看到运行结果了吧。

恭喜你,成功了。

但是这样就可以了吗?显然平时过程中大都不是在命令行下执行的。接下来,我们去看看在eclipse里面会不会一帆风顺捏?

1.       新建一个Web工程。工程名叫做HelloWorld

PS:为什么要建立的是Web工程呢?因为我们不但要在eclipse里面调通,还希望在浏览器中可用。哇哈哈,眼光真远大。

2.       新建一个包叫做com.microsoft.services,以及HelloWorld类。

将刚才的HelloWorld.java中的代码复制进来。最终代码如下:

package com.microsoft.services;

 

public class HelloWorld {

    static

    {

    System.loadLibrary("HelloWorldDll");

    }

    public native static int MyMethod();

 

    public static void main(String[] args)

    {

    HelloWorld hw = new HelloWorld();

    hw.MyMethod();

    }

}

3.       生成class文件。

刚才的代码编辑完成后,如果没有错误,在点击保存的时候eclipse会自动在以下目录中生成对应的class文件:

C:\work\workspace\HelloWorld\WebRoot\WEB-INF\classes\com\microsoft\services

4.       生成h文件。

这一步很关键。如果按照刚才那样到class所在目录下生成的话,是不行的。因为这里需要用到带包编译。在命令行中cd

C:\work\workspace\HelloWorld\WebRoot\WEB-INF\classes\路径下,然后输入:

Javah com.microsoft.services.HelloWorld

成功后会在C:\work\workspace\HelloWorld\WebRoot\WEB-INF\classes下发现:

com_microsoft_services_HelloWorld.h文件。

5.       Vc中建立dll工程。在cpp中添加的代码也有变动:

5.1引入的头文件:#include "com_microsoft_services_HelloWorld.h"

5.2功能实现:

JNIEXPORT jint JNICALL Java_com_microsoft_services_HelloWorld_MyMethod

  (JNIEnv *, jclass);

{

printf("this is helloworld printed in dll");

return 1;

}

6.       编译生成HelloWorldDll.dll。将其放入C:\WINDOWS\system32下,或者放到其他可以让系统通过环境变量找到的位置。

错误1

java.lang.UnsatisfiedLinkError: no HelloWorldDll in java.library.path

    at java.lang.ClassLoader.loadLibrary(Unknown Source)

    at java.lang.Runtime.loadLibrary0(Unknown Source)

    at java.lang.System.loadLibrary(Unknown Source)

    at com.microsoft.services.HelloWorld.<clinit>(HelloWorld.java:6)

Exception in thread "main"

系统没有找到你生成的dll。那表明你的dll放置的位置不对。这里有一个好办法,可以很省事,不用在system32includebin等地方拷来拷去的试验。我们在java代码中添加一句:

System.out.println(System.getProperties().get("java.library.path"));然后将一些代码注释掉。

如下所示:

package com.microsoft.services;

 

public class HelloWorld {

    static

    {

    //System.loadLibrary("HelloWorldDll");

    }

    public native static int MyMethod();

 

    public static void main(String[] args)

    {

       System.out.println(System.getProperties().get("java.library.path"));

    //  HelloWorld hw = new HelloWorld();

    //  hw.MyMethod();

    }

}

运行java程序的时候会看到:

已经将java.library.path显示出来了,我们按指引随意拷贝进去,就可以成功咧!

7.       运行,console里面什么都没出现那么说明成功了,dllprintf的东东是不会显示的。为了验证是否真的成功了,可以打印出方法的返回值,修改HelloWorld.java如下:

 

package com.microsoft.services;

 

public class HelloWorld {

    static

    {

    System.loadLibrary("HelloWorldDll");

    }

    public native static int MyMethod();

 

    public static void main(String[] args)

    {

       //System.out.println(System.getProperties().get("java.library.path"));

       HelloWorld hw = new HelloWorld();

       int t = hw.MyMethod();

       System.out.println(t);

    }

}

 

在执行,就可以看到1已经乖乖显示在了console里。Yeah

最后,以上执行都没有问题的话,将其使用在web上也不成问题了哈哈。打完收工!

posted @ 2010-12-16 18:01  BloodAndBone  Views(10201)  Comments(4Edit  收藏  举报