随笔 - 63  文章 - 0  trackbacks - 0
<2009年5月>
262728293012
3456789
10111213141516
17181920212223
24252627282930
31123456

常用链接

留言簿(2)

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

Java代码
  1. 简单介绍及应用如下:    
  2.   一、JAVA中所需要做的工作    
  3.   在JAVA程序中,首先需要在类中声明所调用的库名称,如下:    
  4.   
  5. static {    
  6. System.loadLibrary(“goodluck”);    
  7. }   
  8.   
  9.   
  10.   在这里,库的扩展名字可以不用写出来,究竟是DLL还是SO,由系统自己判断。    
  11.   
  12.   还需对将要调用的方法做本地声明,关键字为native。且只需要声明,而不需要具体实现。如下:    
  13.   
  14. public native static void set(int i);    
  15. public native static int get();   
  16.   
  17.   
  18.   然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。    
  19.   
  20.   例如程序testdll.java,内容为:    
  21.   
  22. public class testdll    
  23. {    
  24. static    
  25. {    
  26. System.loadLibrary("goodluck");    
  27. }    
  28. public native static int get();    
  29. public native static void set(int i);    
  30. public static void main(String[] args)    
  31. {    
  32. testdll test = new testdll();    
  33. test.set(10);    
  34. System.out.println(test.get());    
  35. }    
  36. }   
  37.   
  38.   
  39.   用javac testdll.java编译它,会生成testdll.class。    
  40.   再用javah testdll,则会在当前目录下生成testdll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。    
  41.   
  42.   二、C/C++中所需要做的工作    
  43.   对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。    
  44.   接上例子。我们先看一下testdll.h文件的内容:    
  45.   
  46. /* DO NOT EDIT THIS FILE - it is machine generated */    
  47. #include    
  48. /* Header for class testdll */    
  49. #ifndef _Included_testdll    
  50. #define _Included_testdll    
  51. #ifdef __cplusplus    
  52. extern "C" {    
  53. #endif    
  54. /*   
  55. * Class: testdll   
  56. * Method: get   
  57. * Signature: ()I   
  58. */    
  59. JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass);    
  60. /*   
  61. * Class: testdll   
  62. * Method: set   
  63. * Signature: (I)V   
  64. */    
  65. JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);    
  66. #ifdef __cplusplus    
  67. }    
  68. #endif    
  69. #endif   
  70.   
  71.   
  72.   在具体实现的时候,我们只关心两个函数原型    
  73.   JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass); 和    
  74.   JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);   
  75.   
  76.   这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVA的int类型与本 地的int沟通的一种类型,我们可以视而不见,就当做int使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数 中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。    
  77.   
  78.   好,下面我们用testdll.cpp文件具体实现这两个函数:    
  79.   
  80. #include "testdll.h"    
  81. int i = 0;    
  82. JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass)    
  83. {    
  84. return i;    
  85. }    
  86. JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j)    
  87. {    
  88. i = j;    
  89. }   
  90.   
  91.   
  92.   编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是goodluck.dll 。把goodluck.dll拷贝到testdll.class的目录下,java testdll运行它,就可以观察到结果了。    
  93.   我的项目比较复杂,需要调用动态链接库,这样在JNI传送参数到C程序时,需要对参数进行处理转换。才可以被C程序识别。   
  94.   大体程序如下:   
  95.   
  96. public class SendSMS {    
  97. static    
  98. {    
  99.   
  100.   
  101.     
  102. System.out.println(System.getProperty("java.library.path"));    
  103. System.loadLibrary("sms");    
  104. }    
  105. public native static int SmsInit();    
  106. public native static int SmsSend(byte[] mobileNo, byte[] smContent);    
  107. }    
  108.   
  109.   在这里要注意的是,path里一定要包含类库的路径,否则在程序运行时会抛出异常:   
  110.   java.lang.UnsatisfiedLinkError: no sms in java.library.path   
  111.   at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1491)   
  112.   at java.lang.Runtime.loadLibrary0(Runtime.java:788)   
  113.   at java.lang.System.loadLibrary(System.java:834)   
  114.   at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)   
  115.   at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)   
  116.   Exception in thread "main"  
  117.   
  118.   指引的路径应该到.dll文件的上一级,如果指到.dll,则会报:   
  119.   java.lang.UnsatisfiedLinkError: C:\sms.dll: Can't find dependent libraries   
  120.   at java.lang.ClassLoader$NativeLibrary.load(Native Method)   
  121.   at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560)   
  122.   at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1485)   
  123.   at java.lang.Runtime.loadLibrary0(Runtime.java:788)   
  124.   at java.lang.System.loadLibrary(System.java:834)   
  125.   at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)   
  126.   Exception in thread "main"  
  127.   
  128.   通过编译,生成com_mobilesoft_sms_mobilesoftinfo_SendSMS.h头文件。(建议使用Jbuilder进行编 译,操作比较简单!)这个头文件就是Java和C之间的纽带。要特别注意的是方法中传递的参数jbyteArray,这在接下来的过程中会重点介绍。   
  129.   
  130. /* DO NOT EDIT THIS FILE - it is machine generated */    
  131. #include    
  132. /* Header for class com_mobilesoft_sms_mobilesoftinfo_SendSMS */    
  133. #ifndef _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS    
  134. #define _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS    
  135. #ifdef __cplusplus    
  136. extern "C" {    
  137. #endif    
  138. /*   
  139. * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS   
  140. * Method: SmsInit   
  141. * Signature: ()I   
  142. */    
  143. JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit    
  144. (JNIEnv *, jclass);    
  145. /*   
  146. * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS   
  147. * Method: SmsSend   
  148. * Signature: ([B[B)I   
  149. */    
  150. JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend    
  151. (JNIEnv *, jclass, jbyteArray, jbyteArray);    
  152. #ifdef __cplusplus    
  153. }    
  154. #endif    
  155. #endif   
  156.   
  157.   
  158.   
  159.   对于我要调用的C程序的动态链接库,C程序也要提供一个头文件,sms.h。这个文件将要调用的方法罗列了出来。   
  160.   
  161. /*   
  162. * SMS API   
  163. * Author: yippit   
  164. * Date: 2004.6.8   
  165. */    
  166. #ifndef MCS_SMS_H    
  167. #define MCS_SMS_H    
  168. #define DLLEXPORT __declspec(dllexport)    
  169. /*sms storage*/    
  170. #define SMS_SIM 0    
  171. #define SMS_MT 1    
  172. /*sms states*/    
  173. #define SMS_UNREAD 0    
  174. #define SMS_READ 1    
  175. /*sms type*/    
  176. #define SMS_NOPARSE -1    
  177. #define SMS_NORMAL 0    
  178. #define SMS_FLASH 1    
  179. #define SMS_MMSNOTI 2    
  180. typedef struct tagSmsEntry {    
  181. int index; /*index, start from 1*/    
  182. int status; /*read, unread*/    
  183. int type; /*-1-can't parser 0-normal, 1-flash, 2-mms*/    
  184. int storage; /*SMS_SIM, SMS_MT*/    
  185. char date[24];    
  186. char number[32];    
  187. char text[144];    
  188. } SmsEntry;    
  189. DLLEXPORT int SmsInit(void);    
  190. DLLEXPORT int SmsSend(char *phonenum, char *content);    
  191. DLLEXPORT int SmsSetSCA(char *sca);    
  192. DLLEXPORT int SmsGetSCA(char *sca);    
  193. DLLEXPORT int SmsSetInd(int ind);    
  194. DLLEXPORT int SmsGetInd(void);    
  195. DLLEXPORT int SmsGetInfo(int storage, int *max, int *used);    
  196. DLLEXPORT int SmsSaveFlash(int flag);    
  197. DLLEXPORT int SmsRead(SmsEntry *entry, int storage, int index);    
  198. DLLEXPORT int SmsDelete(int storage, int index);    
  199. DLLEXPORT int SmsModifyStatus(int storage, int index); /*unread -> read*/    
  200. #endif   
  201.   
  202.   
  203.   在有了这两个头文件之后,就可以进行C程序的编写了。也就是实现对JNI调用的两个方法。在网上的资料中,由于调用的方法实现的都比较简单,(大多是打印字符串等)所以避开了JNI中最麻烦的部分,也是最关键的部分,参数的传递。     
  204. 由于Java和C的编码是不同的,所以传递的参数是要进行再处理,否则C程序是会对参数在编译过程中提出警告,例如;warning C4024: 'SmsSend' : different types for formal and actual parameter 2等。   
  205.   Sms.c的程序如下:   
  206.   
  207. #include "sms.h"    
  208. #include "com_mobilesoft_sms_mobilesoftinfo_SendSMS.h"    
  209. JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit(JNIEnv * env, jclass jobject)    
  210. {    
  211. return SmsInit();    
  212. }    
  213.   
  214. JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend(JNIEnv * env, jclass jobject, jbyteArray mobileno, jbyteArray smscontent)    
  215. {    
  216. char * pSmscontent ;    
  217. //jsize theArrayLengthJ = (*env)->GetArrayLength(env,mobileno);    
  218. jbyte * arrayBody = (*env)->GetByteArrayElements(env,mobileno,0);    
  219. char * pMobileNo = (char *)arrayBody;    
  220. printf("[%s]\n ", pMobileNo);    
  221. //jsize size = (*env)->GetArrayLength(env,smscontent);    
  222. arrayBody = (*env)->GetByteArrayElements(env,smscontent,0);    
  223. pSmscontent = (char *)arrayBody;    
  224. printf("<%s>\n", pSmscontent);    
  225. return SmsSend(pMobileNo,pSmscontent);    
  226. }   
  227.   
  228.   
  229.   
  230.   对于C或C++,在程序上是会有稍微的不同,这可以由读者对其进行适当的修改。这里要注意的是GetArrayLength,GetByteArrayElements等这些JNI中已经包含的方法,这些方法是专门对转换参数类型而提供的。具体的方法有很多,在下一篇中会做专门的介绍。   
  231.   在完成了上述的文件后,可以对sms.c进行编译,生成.dll文件(建议在release中编译,这样动态链接库的容积会比较小!)   
  232.   完成.dll文件的编译后,就可以在Java中调用C程序中的方法了。例如文件test.java   
  233.   
  234. public class test {    
  235. public test() {    
  236. }    
  237. public static void main(String[] args) {    
  238. byte[] mobileno = {    
  239. 0x310x330x360x360x310x360x330x300x360x360x370x00};    
  240. String smscontentemp = "早上好";    
  241. byte[] temp = {0};    
  242. try {    
  243. byte[] smscontentdb = smscontentemp.getBytes("gbk");    
  244. byte[] smscontent = new byte[smscontentdb.length + temp.length];    
  245. System.arraycopy(smscontentdb, 0, smscontent, 0, smscontentdb.length);    
  246. System.arraycopy(temp, 0, smscontent, smscontentdb.length, temp.length);    
  247. SendSMS sendSMS = new SendSMS();    
  248. sendSMS.SmsInit();    
  249. if (sendSMS.SmsSend(mobileno, smscontent) >= 0) {    
  250. System.out.println("chenggong !");    
  251. }    
  252. else {    
  253. System.out.println("shibai !");    
  254. }    
  255. }catch (Exception ex) {}    
  256. }    
  257. }   
  258.   
  259.   
  260.   
  261.   在这个文件中要注意的有一点,就是在传递字节数组到C程序中时,最后的结尾一定要以0结束。这是一个偷懒的做法,不过是个有效的做法。因为大多数情况 下,接口是由第三方提供的。所以我们一般是不知道在C的方法里,具体是怎么处理参数的。而C又是要求数组是有长度。所以,在Java中,如果你不想写程序 传数组的长度,那么在数组中以0结尾就是最方便的方法了。当然,如果有更好的方法也希望大家提出。   
  262.   
  263.   到这里,一个完整的Java通过JNI调用动态链接库的程序就完成了。实际上也不是很复杂。只要多注意一下细节,是很容易得出来的。  
posted on 2009-05-07 11:04 lanxin1020 阅读(167) 评论(0)  编辑  收藏 所属分类: j2se

只有注册用户登录后才能发表评论。


网站导航: