JNI操作的详细步骤

网上有很多关于如何编写dll的帖子,但是真正应用到实际还是需要自己去摸索。现在将自己编写的过程和经验与大家分享。

JNI(Java 本地方法)的操作步骤比较固定,但是在具体的细节上,却会出很多问题。现详述如下:

一、首先创建Java项目,然后新建一个自己要使用的类,在我们的项目中,我们做的是广播加密,Java调用底层的c加密。所以我在Java中新建的类名为:BroadcastEnc.java;

二、在该类中编写本地化方法。为了格式的统一,将本地化方法放在Java方法的前面。因为该本地化方法只有方法头,所以要做好注释,我们本地化方法的一个实例是:

    /**服务器调用该方法初始化广播加密系统,并将相关参数保存在文件systemFileName

*

* 初始化用到的素数等参数保存在文件pairFileName,在这个方法中也确定系统最大

*用户数目

    * @param number:广播加密系统允许的用户个数

    * @param pairFileName:初始化广播加密系统需要的参数

    * @param systemfile:保存广播加密系统的公钥在该文件中,需要传递给用户

* @param myprivkeyfile:在计算用户密钥时,需要用到伽马,但是该数不能保存

*  系统参数中,而应作为一个单独的参数保密

    */

    public native void Setup_broadcast_sys(int number,String pairFileName,String systemfile,String myprivkeyfile);

三、在声明完本地化方法之后,还要在Java程序中加载动态链接库,语句是:

static{

           System.loadLibrary("BroadcastEnc");

    }

如果此时编写main方法进行测试,就会发现Java编译器会报无法加载动态链接库,这就是我们需要做的最主要工作:编写dll文件。

四、编写dll的过程较为复杂,而且该过程不智能,会出现很多问题。平时我们都习惯于用Eclipse编写程序,并运行。实际上,Eclipse在创建每一个项目时,在项目目录中都会包括srcbin文件夹,srcJava源文件,bin放编译之后生成的.class文件。但是要编写dll文件,我们就需要将源文件和字节码放在一块,所以我们不能用Eclipse编译程序,而需要用命令行模式,转到src文件夹,运行javac命令生成.class文件。

五、src文件夹下生成.class文件之后,我们就可以在命令行模式下运行javah,生成相应的.h文件,作为C程序项目的头文件。在运行javah命令时,有可能提示找不到类文件,即使你感觉类文件就在那里。这时的解决方法是:设置环境变量,增加CLASSPATH变量,值就是你此次要编译的项目src所在文件夹的路径名,我的CLASSPATH值为:C:/Documents and Settings/Administrator/workspace/DSMS6/src。这个地方有可能令人疑惑,一般设置环境变量不是都设置javabin目录吗,在此环境变量中还有path变量,这其中包含你原来认为的路径。实际上,设置完毕之后,很有可能还会报错,提示找不到类文件,这时我感觉是环境变量尚未写入系统,重启可以解决这个问题。

六、在第一次操作时,对jni还不是很了解,所有的类文件都放在默认包中。这种方式对一个小的项目来说还是可以的,但是对于一个有很多不同功能的包的大项目就会显得很乱,体现不出模块化的优势。图片一有很多类还放在默认包中,而在图片二中,已经不再使用默认包。

 

要实现对包中的类文件生成.h头文件,其实很简单。只需要在运行javah命令时将包名加在类名之前即可,一个实例是:javah Broadcast.BroadcastEnc,这样就会在src文件夹下生成相应的头文件。这样生成的头文件就会包含包的信息,在每一个本地化方法中都会包含包名。我们项目生成的一个本地化方法是:

JNIEXPORT void JNICALL Java_Broadcast_BroadcastEnc_Setup_1broadcast_1sys

         (JNIEnv *, jobject, jint, jstring, jstring, jstring);

与在Java中声明的方法相比,生成的头文件中,方法名多了类名,包名,和关键词Java。后面还有几个1,这都是自动生成的,不要尝试修改、删除。

七、以上都属于Java项目下的操作,下面需要进行C项目下的操作。首先用VC新建一个空的动态链接库项目,名字就是你在Java下要load的动态链接库名,也就是第三版中的名字:BroadcastEnc,然后将先前生成的头文件放在该项目的Head Files中,然后编写相应的.C文件实现头文件中声明的方法。在这个过程中一定要注意不能修改方法名,直接复制粘贴就行。自动生成的头文件方法中的形式参数只包括类型,不包括变量,所以在.c文件中需要添上变量名,否则也会提示错误。

八、当然,编辑C 项目还会遇到很多其它更加匪夷所思的问题,那属于C语言遇到的问题,在此就不再论述。

九、如果一切运行正常,下面就会生成dll文件,该文件会在C项目的Debug文件夹下,将该dll文件复制到Java项目中,复制时无需复制到src下,只是复制到该项目根目录下即可,如图三。

       以上即是整个JNI步骤,这其中很有可能还会遇到其它问题,但是以上所列是自己已经遇到的问题,也是最为常见的问题,希望对大家有所帮助。

       在此还想多说说关于JNI中字符串参数的传递方法。在Java高级语言中,有String类型,但是在C语言中没有该类型,实现时只能用char *来代替,这其中的转换关系较为复杂,以我们写的一个函数为例来展示转换:

JNIEXPORT void JNICALL Java_Broadcast_BroadcastEnc_Setup_1broadcast_1sys

  (JNIEnv *env, jobject obj, jint number, jstring pairFileName, jstring systemfile, jstring myprivkeyfile)

{

       char *pairFileName2=(char*)(*env)->GetStringUTFChars(env,pairFileName,NULL);

       char *systemfile2=(char*)(*env)->GetStringUTFChars(env,systemfile,NULL);

       char *myprivkeyfile2=(char*)(*env)->GetStringUTFChars(env,myprivkeyfile,NULL);

       Setup_broadcast_sys(number,pairFileName2,systemfile2,myprivkeyfile2);

       printf("初始化广播加密系统成功!!!!/n");

}

       首先,javah会将String类型变为jstring类型,然后我们调用jni中已经存在的字符串转换方法GetStringUTFChars)来将字符串变为char *数组。这里格式固定,可直接套用。

       一般可以用高级语言实现的就都用高级语言实现,并不要为了追求速度而放弃简单性和安全性。JNI调用dll虽说在速度上有了很大提高,但是却不简单,而且这其中遇到的错误让人呕吐。不仅如此,在出现问题时,Java给你的错误提示根本就读不懂,所以能少用就少用。但是如果已经用C语言写过,不想再重复开发车轮,则可以尝试利用JNI来提高性能。

posted on 2010-07-20 10:17  哼哼唧唧  阅读(223)  评论(0编辑  收藏  举报

导航