锁定老帖子 主题:如何在Java中调用dll文件
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-04-24
如何在Java中调用dll文件我知道的有两种方法,一种是直接用JNI,另一种是则是用Jacob(实质上也用的是JNI) 先讲讲第一种方法 1.编写java程序TestDll,注意,这个类有两个作用,一个是用来做头文件,另外一个作用就是通过它来调用dll 2.编译: javac TestDll #ifndef _Included_TestDll #ifdef __cplusplus
明来写: #include "jni_md.h" JNIEXPORT jint JNICALL Java_TestDll_DoubleValue (JNIEnv *, jclass, jint p) JNIEXPORT,JNICALL,JNIEnv *, jclass请都不要动,jint对应是java里的int 别忘了把TestDll.h,jni.h(这个文件在jdk/include下),jni_md.h(这个文件在jdk/include/win32下)复制到VC 工程目录中,然后编译生成dll 5.把生成的DLLSample.dll复制到jdk/bin下(确保PATH指向了这个目录) 6.应用 以得到正确结果,这里体现国TestDll的第二个作用,它是调用dll的一个纽带
因为必须要和java里面的声明一致才行 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2007-04-24
如何用JNI技术提高Java的性能详解
阻碍Java获得广泛应用的一个主要因素是Java程序的运行效率。Java是介于解释型和编译型之间的一种语言,同样的程序,如果用编译型语言C来实现,其运行速度一般要比Java快一倍以上。Java具有平台无关性,这使人们在开发企业级应用的时候总是把它作为主要候选方案之一,但是性能方面的因素又大大削弱了它的竞争力。为此,提高Java的性能就显得十分重要。 问题的提出 Sun公司及Java的支持者们为提高Java的运行速度已经做出了许多努力,其中大多数集中在程序设计的方法和模式选择方面。由于算法和设计模式的优化是通用的,对Java有效的优化算法和设计模式,对其他编译语言也基本同样适用,因此不能从根本上改变Java程序与编译型语言在执行效率方面的差异。 JIT(Just In Time,及时编译)技术是个比较好的思想。它的基本原理是:首先通过Java编译器把Java源代码编译成平台无关的二进制字节码。然后在Java程序真正执行之前,系统通过JIT编译器把Java的字节码编译为本地化机器码。最后,系统执行本地化机器码,节省了对字节码进行解释的时间。这样做的优点是大大提高了Java程序的性能,缩短了加载程序的时间;同时,由于编译的结果并不在程序运行间保存,因此也节约了存储空间。缺点是由于JIT编译器对所有的代码都想优化,因此同样也占用了很多时间。 动态优化技术是提高Java性能的另一个尝试。该技术试图通过把Java源程序直接编译成机器码,以充分利用Java动态编译和静态编译技术来提高Java的性能。该方法把输入的Java源码或字节码转换为经过高度优化的可执行代码和动态库 (Windows中的. dll文件或Unix中的. so文件)。该技术能大大提高程序的性能,但却破坏了Java的可移植性。 JNI技术 实际上,有一种通常为我们忽视的技术可以在很大程度上解决这个难题,那就是JNI(Java Native Interface, Java本地化方法)。主张采用纯Java的人们通常反对本地化代码的使用,他们认为在Java程序执行的过程中调用C/C++程序会影响程序的可移植性和安全性。还有一些人认为JNI只是对过去混合编程技术的简单扩展,其实际目的是为了充分利用大量原有的C程序库。 其实,我们不必拘泥于严格的平台独立性限制,因为采用JNI技术只是针对一些严重影响Java性能的代码段,该部分可能只占源程序的极少部分,所以几乎可以不考虑该部分代码在主流平台之间移植的工作量。同时,也不必过分担心类型匹配问题,我们完全可以控制代码不出现这种错误。此外,也不必担心安全控制问题,因为Java安全模型已扩展为允许非系统类加载和调用本地方法。根据Java规范,从JDK 1. 2开始,FindClass将设法找到与当前的本地方法关联的类加载器。如果平台相关代码属于一个系统类,则无需涉及任何类加载器; 否则,将调用适当的类加载器来加载和链接已命名的类。换句话说,如果在Java程序中直接调用C/C++语言产生的机器码,该部分代码的安全性就由Java虚拟机控制。 JNI实现步骤 编写JNI代码的大致流程如下图所示: JNI实现流程图 1. 首先编写需要JNI功能的Java类源文件。其中,需要JNI实现的方法应当用native关键字声明。在该类中,用System. loadLibrary()方法加载需要的动态链接库。关键代码如下: //Compute.java …… public class Compute { public native double comp (double [] params); …… static { // 调用动态链接库 System. loadLibrary(“mathlib”); } …… } 2. 将该类源文件用Java类编译器编译成二进制字节码文件。由于采用了native关键字声明,编译器会忽视没有代码体的JNI方法部分。 3. 利用javah -jni *.class 生成相关JNI方法的头文件。我们可以手工生成该文件,但是由于Java虚拟机是根据一定的命名规范完成对JNI方法的调用,所以手工编写头文件需要特别小心。 上述文件产生的头文件部分代码如下: //Compute. h …… extern “C” { JNIEXPORT jdouble JNICALL Java_Compute_comp (JNIEnv *, jobject, jdoubleArray); } …… 可以看出,JNI函数名称分为三部分:首先是Java关键字,供Java虚拟机识别;然后是调用者类名称(全限定的类名,其中用下划线代替名称分隔符);最后是对应的方法名称,各段名称之间用下划线分割。 JNI函数的参数也由三部分组成: 首先是JNIEnv *,是一个指向JNI运行环境的指针;第二个参数随本地方法是静态还是非静态而有所不同——非静态本地方法的第二个参数是对对象的引用,而静态本地方法的第二个参数是对其 Java 类的引用; 其余的参数对应通常 Java 方法的参数,参数类型需要根据一定规则进行映射。 4. 根据头文件编写相应方法的实现代码。由于篇幅所限,具体的实现部分在此不再赘述。在编码过程中,需要注意变量的长度问题,例如Java的整型变量长度为32位,而C语言为16位,所以要仔细核对变量类型映射表,防止在传值过程中出现问题。 5. 利用C/C++编译器将JNI实现代码编译成动态链接库。调用者类中需要显式调用该链接库。 在Win32环境下,可以利用Visual C ++或其他能产生DLL文件的C/C++编译器将实现代码编译成动态链接库。笔者利用的是Microsoft.NET Framework的编译器。编译指令如下,其中%Java_HOME%是笔者的jdk安装目录变量: cl -I%Java_HOME%\include -I%Java_HOME%\include\win32 -LD jnicomp. c -Femathlib. dll 在Sun Soloaris下,相应指令为: cc -G -I/usr/local/java/include -I/usr/local/java/include/solaris jnicomp. c \ -o mathlib. so 注意,编译的时候需要用I指令包含必要的库文件路径。 经过上述处理,就基本上完成了一个包含本地化方法的Java类的开发。 JNI技术的应用 一些主要的Java技术,如JDBC和RMI,大部分都采用JNI方式实现。但是,采用JNI确实会影响程序的平台无关性,所以只能在特别需要的地方才能使用。通常来说,如果遇到下面的情况,我们可以考虑JNI: ● 需要直接操作物理设备,而没有相关的驱动程序,这时候我们可能需要用C甚至汇编语言来编写该设备的驱动,然后通过JNI调用; ● 涉及大量数学运算的部分,用Java会带来些效率上的损失; ● 用Java会产生系统难以支付的开销,如需要大量网络链接的场合; ● 存在大量可重用的C/C++代码,通过JNI可以减少开发工作量,避免重复开发。 另外,在利用JNI技术的时候要注意以下几点: ● 由于Java安全机制的限制,不要试图通过Jar文件的方式发布包含本地化方法的Applet到客户端; ● 注意内存管理问题,虽然在本地方法返回 Java 后将自动释放局部引用,但过多的局部引用将使虚拟机在执行本地方法时耗尽内存; ● JNI技术不仅可以让Java程序调用C/C++代码,也可以让C/C++代码调用Java代码。 |
|
返回顶楼 | |
发表时间:2007-04-24
JNI完全手册
JNI完全手册(一) yippit 原创 最近在公司里做了一个手机的项目,需要JAVA程序在发送短信的时候和第三方的短信服务器连接。短信接口是用C++写的。琢磨了三天,大致搞懂了JNI的主体部分。先将心得整理,希望各位朋友少走弯路。 首先引用一篇文章,介绍一个简单的JNI的调用的过程。 JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。 JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。 简单介绍及应用如下: 一、JAVA中所需要做的工作 在JAVA程序中,首先需要在类中声明所调用的库名称,如下: static { System.loadLibrary(“goodluck”); } 在这里,库的扩展名字可以不用写出来,究竟是DLL还是SO,由系统自己判断。 还需要对将要调用的方法做本地声明,关键字为native。并且只需要声明,而不需要具 体实现。如下: public native static void set(int i); public native static int get(); 然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。 例如程序testdll.java,内容为: public class testdll { static { System.loadLibrary("goodluck"); } public native static int get(); public native static void set(int i); public static void main(String[] args) { testdll test = new testdll(); test.set(10); System.out.println(test.get()); } } 用javac testdll.java编译它,会生成testdll.class。 再用javah testdll,则会在当前目录下生成testdll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。 二、C/C++中所需要做的工作 对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。 接上例子。我们先看一下testdll.h文件的内容: /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* 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 JNICALL Java_testdll_get (JNIEnv *, jclass); 和 JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint); 这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVA的int类型与本地的int沟通的一种类型,我们可以视而不见,就当做int使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。 好,下面我们用testdll.cpp文件具体实现这两个函数: #include "testdll.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; } 编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是goodluck.dll 。把goodluck.dll拷贝到testdll.class的目录下,java testdll运行它,就可以观察到结果了。 我的项目比较复杂,需要调用动态链接库,这样在JNI传送参数到C程序时,需要对参数进行处理转换。才可以被C程序识别。 大体程序如下: public class SendSMS { static { System.out.println(System.getProperty("java.library.path")); System.loadLibrary("sms"); } public native static int SmsInit(); public native static int SmsSend(byte[] mobileNo, byte[] smContent); } 在这里要注意的是,path里一定要包含类库的路径,否则在程序运行时会抛出异常: java.lang.UnsatisfiedLinkError: no sms in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1491) at java.lang.Runtime.loadLibrary0(Runtime.java:788) at java.lang.System.loadLibrary(System.java:834) at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14) at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18) Exception in thread "main" 指引的路径应该到.dll文件的上一级,如果指到.dll,则会报: java.lang.UnsatisfiedLinkError: C:\sms.dll: Can't find dependent libraries at java.lang.ClassLoader$NativeLibrary.load(Native Method) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1485) at java.lang.Runtime.loadLibrary0(Runtime.java:788) at java.lang.System.loadLibrary(System.java:834) at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14) at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18) Exception in thread "main" 通过编译,生成com_mobilesoft_sms_mobilesoftinfo_SendSMS.h头文件。(建议使用Jbuilder进行编译,操作比较简单!)这个头文件就是Java和C之间的纽带。要特别注意的是方法中传递的参数jbyteArray,这在接下来的过程中会重点介绍。 /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_mobilesoft_sms_mobilesoftinfo_SendSMS */ #ifndef _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS #define _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS #ifdef __cplusplus extern "C" { #endif /* * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS * Method: SmsInit * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit (JNIEnv *, jclass); /* * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS * Method: SmsSend * Signature: ([B[B)I */ JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend (JNIEnv *, jclass, jbyteArray, jbyteArray); #ifdef __cplusplus } #endif #endif 对于我要调用的C程序的动态链接库,C程序也要提供一个头文件,sms.h。这个文件将要调用的方法罗列了出来。 /* * SMS API * Author: yippit * Date: 2004.6.8 */ #ifndef MCS_SMS_H #define MCS_SMS_H #define DLLEXPORT __declspec(dllexport) /*sms storage*/ #define SMS_SIM 0 #define SMS_MT 1 /*sms states*/ #define SMS_UNREAD 0 #define SMS_READ 1 /*sms type*/ #define SMS_NOPARSE -1 #define SMS_NORMAL 0 #define SMS_FLASH 1 #define SMS_MMSNOTI 2 typedef struct tagSmsEntry { int index; /*index, start from 1*/ int status; /*read, unread*/ int type; /*-1-can't parser 0-normal, 1-flash, 2-mms*/ int storage; /*SMS_SIM, SMS_MT*/ char date[24]; char number[32]; char text[144]; } SmsEntry; DLLEXPORT int SmsInit(void); DLLEXPORT int SmsSend(char *phonenum, char *content); DLLEXPORT int SmsSetSCA(char *sca); DLLEXPORT int SmsGetSCA(char *sca); DLLEXPORT int SmsSetInd(int ind); DLLEXPORT int SmsGetInd(void); DLLEXPORT int SmsGetInfo(int storage, int *max, int *used); DLLEXPORT int SmsSaveFlash(int flag); DLLEXPORT int SmsRead(SmsEntry *entry, int storage, int index); DLLEXPORT int SmsDelete(int storage, int index); DLLEXPORT int SmsModifyStatus(int storage, int index); /*unread -> read*/ #endif 在有了这两个头文件之后,就可以进行C程序的编写了。也就是实现对JNI调用的两个方法。在网上的资料中,由于调用的方法实现的都比较简单,(大多是打印字符串等)所以避开了JNI中最麻烦的部分,也是最关键的部分,参数的传递。由于Java和C的编码是不同的,所以传递的参数是要进行再处理,否则C程序是会对参数在编译过程中提出警告,例如;warning C4024: 'SmsSend' : different types for formal and actual parameter 2等。 Sms.c的程序如下: #include "sms.h" #include "com_mobilesoft_sms_mobilesoftinfo_SendSMS.h" JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit(JNIEnv * env, jclass jobject) { return SmsInit(); } JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend(JNIEnv * env, jclass jobject, jbyteArray mobileno, jbyteArray smscontent) { char * pSmscontent ; //jsize theArrayLengthJ = (*env)->GetArrayLength(env,mobileno); jbyte * arrayBody = (*env)->GetByteArrayElements(env,mobileno,0); char * pMobileNo = (char *)arrayBody; printf("[%s]\n ", pMobileNo); //jsize size = (*env)->GetArrayLength(env,smscontent); arrayBody = (*env)->GetByteArrayElements(env,smscontent,0); pSmscontent = (char *)arrayBody; printf("<%s>\n", pSmscontent); return SmsSend(pMobileNo,pSmscontent); } 对于C或C++,在程序上是会有稍微的不同,这可以由读者对其进行适当的修改。这里要注意的是GetArrayLength,GetByteArrayElements等这些JNI中已经包含的方法,这些方法是专门对转换参数类型而提供的。具体的方法有很多,在下一篇中会做专门的介绍。 在完成了上述的文件后,可以对sms.c进行编译,生成.dll文件(建议在release中编译,这样动态链接库的容积会比较小!) 完成.dll文件的编译后,就可以在Java中调用C程序中的方法了。例如文件test.java public class test { public test() { } public static void main(String[] args) { byte[] mobileno = { 0x31, 0x33, 0x36, 0x36, 0x31, 0x36, 0x33, 0x30, 0x36, 0x36, 0x37, 0x00}; String smscontentemp = "早上好"; byte[] temp = {0}; try { byte[] smscontentdb = smscontentemp.getBytes("gbk"); byte[] smscontent = new byte[smscontentdb.length + temp.length]; System.arraycopy(smscontentdb, 0, smscontent, 0, smscontentdb.length); System.arraycopy(temp, 0, smscontent, smscontentdb.length, temp.length); SendSMS sendSMS = new SendSMS(); sendSMS.SmsInit(); if (sendSMS.SmsSend(mobileno, smscontent) >= 0) { System.out.println("chenggong !"); } else { System.out.println("shibai !"); } }catch (Exception ex) {} } } 在这个文件中要注意的有一点,就是在传递字节数组到C程序中时,最后的结尾一定要以0结束。这是一个偷懒的做法,不过是个有效的做法。因为大多数情况下,接口是由第三方提供的。所以我们一般是不知道在C的方法里,具体是怎么处理参数的。而C又是要求数组是有长度。所以,在Java中,如果你不想写程序传数组的长度,那么在数组中以0结尾就是最方便的方法了。当然,如果有更好的方法也希望大家提出。 到这里,一个完整的Java通过JNI调用动态链接库的程序就完成了。实际上也不是很复杂。只要多注意一下细节,是很容易得出来的。 我的项目比较复杂,需要调用动态链接库,这样在JNI传送参数到C程序时,需要对参数进行处理转换。才可以被C程序识别。 大体程序如下: public class SendSMS { static { System.out.println(System.getProperty("java.library.path")); System.loadLibrary("sms"); } public native static int SmsInit(); public native static int SmsSend(byte[] mobileNo, byte[] smContent); } 在这里要注意的是,path里一定要包含类库的路径,否则在程序运行时会抛出异常: java.lang.UnsatisfiedLinkError: no sms in java.library.path at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1491) at java.lang.Runtime.loadLibrary0(Runtime.java:788) at java.lang.System.loadLibrary(System.java:834) at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14) at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18) Exception in thread "main" 指引的路径应该到.dll文件的上一级,如果指到.dll,则会报: java.lang.UnsatisfiedLinkError: C:\sms.dll: Can't find dependent libraries at java.lang.ClassLoader$NativeLibrary.load(Native Method) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1485) at java.lang.Runtime.loadLibrary0(Runtime.java:788) at java.lang.System.loadLibrary(System.java:834) at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14) at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18) Exception in thread "main" 通过编译,生成com_mobilesoft_sms_mobilesoftinfo_SendSMS.h头文件。(建议使用Jbuilder进行编译,操作比较简单!)这个头文件就是Java和C之间的纽带。要特别注意的是方法中传递的参数jbyteArray,这在接下来的过程中会重点介绍。 /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class com_mobilesoft_sms_mobilesoftinfo_SendSMS */ #ifndef _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS #define _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS #ifdef __cplusplus extern "C" { #endif /* * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS * Method: SmsInit * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit (JNIEnv *, jclass); /* * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS * Method: SmsSend * Signature: ([B[B)I */ JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend (JNIEnv *, jclass, jbyteArray, jbyteArray); #ifdef __cplusplus } #endif #endif 对于我要调用的C程序的动态链接库,C程序也要提供一个头文件,sms.h。这个文件将要调用的方法罗列了出来。 /* * SMS API * Author: yippit * Date: 2004.6.8 */ #ifndef MCS_SMS_H #define MCS_SMS_H #define DLLEXPORT __declspec(dllexport) /*sms storage*/ #define SMS_SIM 0 #define SMS_MT 1 /*sms states*/ #define SMS_UNREAD 0 #define SMS_READ 1 /*sms type*/ #define SMS_NOPARSE -1 #define SMS_NORMAL 0 #define SMS_FLASH 1 #define SMS_MMSNOTI 2 typedef struct tagSmsEntry { int index; /*index, start from 1*/ int status; /*read, unread*/ int type; /*-1-can't parser 0-normal, 1-flash, 2-mms*/ int storage; /*SMS_SIM, SMS_MT*/ char date[24]; char number[32]; char text[144]; } SmsEntry; DLLEXPORT int SmsInit(void); DLLEXPORT int SmsSend(char *phonenum, char *content); DLLEXPORT int SmsSetSCA(char *sca); DLLEXPORT int SmsGetSCA(char *sca); DLLEXPORT int SmsSetInd(int ind); DLLEXPORT int SmsGetInd(void); DLLEXPORT int SmsGetInfo(int storage, int *max, int *used); DLLEXPORT int SmsSaveFlash(int flag); DLLEXPORT int SmsRead(SmsEntry *entry, int storage, int index); DLLEXPORT int SmsDelete(int storage, int index); DLLEXPORT int SmsModifyStatus(int storage, int index); /*unread -> read*/ #endif 在有了这两个头文件之后,就可以进行C程序的编写了。也就是实现对JNI调用的两个方法。在网上的资料中,由于调用的方法实现的都比较简单,(大多是打印字符串等)所以避开了JNI中最麻烦的部分,也是最关键的部分,参数的传递。由于Java和C的编码是不同的,所以传递的参数是要进行再处理,否则C程序是会对参数在编译过程中提出警告,例如;warning C4024: 'SmsSend' : different types for formal and actual parameter 2等。 Sms.c的程序如下: #include "sms.h" #include "com_mobilesoft_sms_mobilesoftinfo_SendSMS.h" JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit(JNIEnv * env, jclass jobject) { return SmsInit(); } JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend(JNIEnv * env, jclass jobject, jbyteArray mobileno, jbyteArray smscontent) { char * pSmscontent ; //jsize theArrayLengthJ = (*env)->GetArrayLength(env,mobileno); jbyte * arrayBody = (*env)->GetByteArrayElements(env,mobileno,0); char * pMobileNo = (char *)arrayBody; printf("[%s]\n ", pMobileNo); //jsize size = (*env)->GetArrayLength(env,smscontent); arrayBody = (*env)->GetByteArrayElements(env,smscontent,0); pSmscontent = (char *)arrayBody; printf("<%s>\n", pSmscontent); return SmsSend(pMobileNo,pSmscontent); } 对于C或C++,在程序上是会有稍微的不同,这可以由读者对其进行适当的修改。这里要注意的是GetArrayLength,GetByteArrayElements等这些JNI中已经包含的方法,这些方法是专门对转换参数类型而提供的。具体的方法有很多,在下一篇中会做专门的介绍。 在完成了上述的文件后,可以对sms.c进行编译,生成.dll文件(建议在release中编译,这样动态链接库的容积会比较小!) 完成.dll文件的编译后,就可以在Java中调用C程序中的方法了。例如文件test.java public class test { public test() { } public static void main(String[] args) { byte[] mobileno = { 0x31, 0x33, 0x36, 0x36, 0x31, 0x36, 0x33, 0x30, 0x36, 0x36, 0x37, 0x00}; String smscontentemp = "早上好"; byte[] temp = {0}; try { byte[] smscontentdb = smscontentemp.getBytes("gbk"); byte[] smscontent = new byte[smscontentdb.length + temp.length]; System.arraycopy(smscontentdb, 0, smscontent, 0, smscontentdb.length); System.arraycopy(temp, 0, smscontent, smscontentdb.length, temp.length); SendSMS sendSMS = new SendSMS(); sendSMS.SmsInit(); if (sendSMS.SmsSend(mobileno, smscontent) >= 0) { System.out.println("chenggong !"); } else { System.out.println("shibai !"); } }catch (Exception ex) {} } } 在这个文件中要注意的有一点,就是在传递字节数组到C程序中时,最后的结尾一定要以0结束。这是一个偷懒的做法,不过是个有效的做法。因为大多数情况下,接口是由第三方提供的。所以我们一般是不知道在C的方法里,具体是怎么处理参数的。而C又是要求数组是有长度。所以,在Java中,如果你不想写程序传数组的长度,那么在数组中以0结尾就是最方便的方法了。当然,如果有更好的方法也希望大家提出。 到这里,一个完整的Java通过JNI调用动态链接库的程序就完成了。实际上也不是很复杂。只要多注意一下细节,是很容易得出来的。 |
|
返回顶楼 | |
发表时间:2007-04-24
这两天刚好整这玩样,搞得哭死
|
|
返回顶楼 | |
发表时间:2007-04-24
转的时候要再排排版。。。。
|
|
返回顶楼 | |
发表时间:2007-04-24
谢谢忠告,本没打算有人会细看的。
下次注意,我会写些自己开发的代码的。 |
|
返回顶楼 | |
发表时间:2007-04-24
爱我靠边站 写道 谢谢忠告,本没打算有人会细看的。
主要是要把代码用代码框再框一次。。。下次注意,我会写些自己开发的代码的。 没格式的代码现在已经看不明白了。。。 |
|
返回顶楼 | |
发表时间:2007-10-16
唉 关于运行dll文件 发现网上都是需要嵌套C的代码 有没有纯java搞出来的吖
|
|
返回顶楼 | |
发表时间:2007-10-16
唉 关于运行dll文件 发现网上都是需要嵌套C的代码 有没有纯java搞出来的吖
|
|
返回顶楼 | |
发表时间:2007-10-16
唉 关于运行dll文件 发现网上都是需要嵌套C的代码 有没有纯java搞出来的吖
|
|
返回顶楼 | |
浏览 18680 次