




已阅读5页,还剩15页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
JNI详细介绍/skywalker256/article/details/4677644JNI其实是Java Native Interface的简称,也就是java本地接口。它提供了若干的API实现了和Java和其他语言的通信(主要是C&C+)。也许不少人觉得Java已经足够强大,为什么要需要JNI这种东西呢?我们知道Java是一种平台无关性的语言,平台对于上层的java代码来说是透明的,所以在多数时间我们是不需要JNI的,但是假如你遇到了如下的三种情况之一呢?1. 你的Java代码,需要得到一个文件的属性。但是你找遍了JDK帮助文档也找不到相关的API。2. 在本地还有一个别的系统,不过他不是Java语言实现的,这个时候你的老板要求你把两套系统整合到一起。3. 你的Java代码中需要用到某种算法,不过算法是用C实现并封装在动态链接库文件(DLL)当中的。对于上述的三种情况,如果没有JNI的话,那就会变得异常棘手了。就算找到解决方案了,也是费时费力。其实说到底还是会增加开发和维护的成本。说了那么多一通废话,现在进入正题。看过JDK源代码的人肯定会注意到在源码里有很多标记成native的方法。这些个方法只有方法签名但是没有方法体。其实这些naive方法就是我们说的 java native interface。他提供了一个调用(invoke)的接口,然后用C或者C+去实现。我们首先来编写这个“桥梁”.我自己的开发环境是 j2sdk1.4.2_15 + eclipse 3.2 + VC+ 6.0,先在eclipse里建立一个HelloFore的Java工程,然后编写下面的代码。Java代码1. packagecom.chnic.jni;2. 3. publicclassSayHellotoCPP4. 5. publicSayHellotoCPP()6. 7. publicnativevoidsayHello(Stringname);8. javaview plaincopy1. packagecom.chnic.jni;2. 3. publicclassSayHellotoCPP4. 5. publicSayHellotoCPP()6. 7. publicnativevoidsayHello(Stringname);8. 一般的第一个程序总是HelloWorld。今天换换口味,把world换成一个名字。我的native本地方法有一个String的参数。会传递一个name到后台去。本地方法已经完成,现在来介绍下javah这个方法,接下来就要用javah方法来生成一个相对应的.h头文件。javah是一个专门为JNI生成头文件的一个命令。CMD打开控制台之后输入javah回车就能看到javah的一些参数。在这里就不多介绍我们要用的是 -jni这个参数,这个参数也是默认的参数,他会生成一个JNI式的.h头文件。在控制台进入到工程的根目录,也就是HelloFore这个目录,然后输入命令。Java代码1. javah-jnicom.chnic.jni.SayHellotoCPPjavaview plaincopy1. javah-jnicom.chnic.jni.SayHellotoCPP命令执行完之后在工程的根目录就会发现com_chnic_jni_SayHellotoCPP.h这个头文件。在这里有必要多句嘴,在执行javah的时候,要输入完整的包名+类名。否则在以后的测试调用过程中会发生java.lang.UnsatisfiedLinkError这个异常。到这里java部分算是基本完成了,接下来我们来编写后端的C+代码。(用C也可以,只不过cout比printf用起来更快些,所以这里俺偷下懒用C+)打开VC+首先新建一个Win32 Dynamic-Link library工程,之后选择An empty DLL project空工程。在这里我C+的工程是HelloEnd,把刚刚生成的那个头文件拷贝到这个工程的根目录里。随便用什么文本编辑器打开这个头文件,发现有一个如下的方法签名。Cpp代码1. /*2. *Class:com_chnic_jni_SayHellotoCPP3. *Method:sayHello4. *Signature:(Ljava/lang/String;)V5. */6. JNIEXPORTvoidJNICALLJava_com_chnic_jni_SayHellotoCPP_sayHello7. (JNIEnv*,jobject,jstring);cppview plaincopy1. /*2. *Class:com_chnic_jni_SayHellotoCPP3. *Method:sayHello4. *Signature:(Ljava/lang/String;)V5. */6. JNIEXPORTvoidJNICALLJava_com_chnic_jni_SayHellotoCPP_sayHello7. (JNIEnv*,jobject,jstring);仔细观察一下这个方法,在注释上标注类名、方法名、签名(Signature),至于这个签名是做什么用的,我们以后再说。在这里最重要的是 Java_com_chnic_jni_SayHellotoCPP_sayHello这个方法签名。在Java端我们执行 sayHello(String name)这个方法之后,JVM就会帮我们唤醒在DLL里的Java_com_chnic_jni_SayHellotoCPP_sayHello这个方法。因此我们新建一个C+ source file来实现这个方法。Cpp代码1. #include2. #includecom_chnic_jni_SayHellotoCPP.h3. 4. 5. JNIEXPORTvoidJNICALLJava_com_chnic_jni_SayHellotoCPP_sayHello6. (JNIEnv*env,jobjectobj,jstringname)7. 8. constchar*pname=env-GetStringUTFChars(name,NULL);9. coutHello,pnameendl;10. cppview plaincopy1. #include2. #includecom_chnic_jni_SayHellotoCPP.h3. 4. 5. JNIEXPORTvoidJNICALLJava_com_chnic_jni_SayHellotoCPP_sayHello6. (JNIEnv*env,jobjectobj,jstringname)7. 8. constchar*pname=env-GetStringUTFChars(name,NULL);9. coutHello,pnameOptions-Directories里设置。F7编译工程发现缺少jni.h这个头文件。这个头文件可以在%JAVA_HOME%/include目录下找到。把这个文件拷贝到C+工程目录,继续编译发现还是找不到。原来是因为在我们刚刚生成的那个头文件里,jni.h这个文件是被 #include 引用进来的,因此我们把尖括号改成双引号#include jni.h,继续编译发现少了jni_md.h文件,接着在%JAVA_HOME%/include/win32下面找到那个头文件,放入到工程根目录,F7编译成功。在Debug目录里会发现生成了HelloEnd.dll这个文件。这个时候后端的C+代码也已经完成,接下来的任务就是怎么把他们连接在一起了,要让前端的java程序“认识并找到”这个动态链接库,就必须把这个DLL放在windows path环境变量下面。有两种方法可以做到:1. 把这个DLL放到windows下面的sysytem32文件夹下面,这个是windows默认的path2. 复制你工程的Debug目录,我这里是C:/Program Files/Microsoft Visual Studio/MyProjects/HelloEnd/Debug这个目录,把这个目录配置到User variable的Path下面。重启eclipse,让eclipse在启动的时候重新读取这个path变量。比较起来,第二种方法比较灵活,在开发的时候不用来回copy dll文件了,节省了很多工作量,所以在开发的时候推荐用第二种方法。在这里我们使用的也是第二种,eclipse重启之后打开 SayHellotoCPP这个类。其实我们上面做的那些是不是是让JVM能找到那些DLL文件,接下来我们要让我们自己的java代码“认识”这个动态链接库。加入System.loadLibrary(HelloEnd);这句到静态初始化块里。Java代码1. packagecom.chnic.jni;2. 3. publicclassSayHellotoCPP4. 5. static6. System.loadLibrary(HelloEnd);7. 8. publicSayHellotoCPP()9. 10. publicnativevoidsayHello(Stringname);11. 12. javaview plaincopy1. packagecom.chnic.jni;2. 3. publicclassSayHellotoCPP4. 5. static6. System.loadLibrary(HelloEnd);7. 8. publicSayHellotoCPP()9. 10. publicnativevoidsayHello(Stringname);11. 12. 这样我们的代码就能认识并加载这个动态链接库文件了。万事俱备,只欠测试代码了,接下来编写测试代码。Java代码1. SayHellotoCPPshp=newSayHellotoCPP();2. shp.sayHello(World);javaview plaincopy1. SayHellotoCPPshp=newSayHellotoCPP();2. shp.sayHello(World);我们不让他直接Hello,World。我们把World传进去,执行代码。发现控制台打印出来Hello, World这句话。就此一个最简单的JNI程序已经开发完成。也许有朋友会对CPP代码里的Cpp代码1. constchar*pname=env-GetStringUTFChars(name,NULL);cppview plaincopy1. constchar*pname=env-GetStringUTFChars(name,NULL);这句有疑问,这个GetStringUTFChars就是JNI给developer提供的API,我们以后再讲。在这里不得不多句嘴。1. 因为JNI有一个Native这个特点,一点有项目用了JNI,也就说明这个项目基本不能跨平台了。2. JNI调用是相当慢的,在实际使用的之前一定要先想明白是否有这个必要。3. 因为C+和C这样的语言非常灵活,一不小心就容易出错,比如我刚刚的代码就没有写析构字符串释放内存,对于java developer来说因为有了GC 垃圾回收机制,所以大多数人没有写析构函数这样的概念。所以JNI也会增加程序中的风险,增大程序的不稳定性。其实在Java代码中,除了对本地方法标注native关键字和加上要加载动态链接库之外,JNI基本上是对上层coder透明的,上层coder调用那些本地方法的时候并不知道这个方法的方法体究竟是在哪里,这个道理就像我们用JDK所提供的API一样。所以在Java中使用JNI还是很简单的,相比之下在C+中调用java,就比前者要复杂的多了。现在来介绍下JNI里的数据类型。在C+里,编译器会很据所处的平台来为一些基本的数据类型来分配长度,因此也就造成了平台不一致性,而这个问题在Java中则不存在,因为有JVM的缘故,所以Java中的基本数据类型在所有平台下得到的都是相同的长度,比如int的宽度永远都是32位。基于这方面的原因,java和c+的基本数据类型就需要实现一些mapping,保持一致性。下面的表可以概括: Java类型 本地类型 JNI中定义的别名 intlongjintlong_int64jlongbytesigned charjbytebooleanunsigned charjbooleancharunsigned shortjcharshortshortjshortfloatfloatjfloatdoubledoublejdoubleObject_jobject*jobject上面的表格是我在网上搜的,放上来给大家对比一下。对于每一种映射的数据类型,JNI的设计者其实已经帮我们取好了相应的别名以方便记忆。如果想了解一些更加细致的信息,可以去看一些jni.h这个头文件,各种数据类型的定义以及别名就被定义在这个文件中。了解了JNI中的数据类型,下面就来看这次的例子。这次我们用Java来实现一个前端的market(以下就用Foreground代替)用CPP来实现一个后端factory(以下用backend代替)。我们首先还是来编写包含本地方法的java类。Java代码1. packagecom.chnic.service;2. 3. importcom.chnic.bean.Order;4. 5. publicclassBusiness6. static7. System.loadLibrary(FruitFactory);8. 9. 10. publicBusiness()11. 12. 13. 14. publicnativedoublegetPrice(Stringname);15. publicnativeOrdergetOrder(Stringname,intamount);16. publicnativeOrdergetRamdomOrder();17. publicnativevoidanalyzeOrder(Orderorder);18. 19. publicvoidnotification()20. System.out.println(Gotanotification.);21. 22. 23. publicstaticvoidnotificationByStatic()24. System.out.println(Gotanotificationinastaticmethod.);25. 26. javaview plaincopy1. packagecom.chnic.service;2. 3. importcom.chnic.bean.Order;4. 5. publicclassBusiness6. static7. System.loadLibrary(FruitFactory);8. 9. 10. publicBusiness()11. 12. 13. 14. publicnativedoublegetPrice(Stringname);15. publicnativeOrdergetOrder(Stringname,intamount);16. publicnativeOrdergetRamdomOrder();17. publicnativevoidanalyzeOrder(Orderorder);18. 19. publicvoidnotification()20. System.out.println(Gotanotification.);21. 22. 23. publicstaticvoidnotificationByStatic()24. System.out.println(Gotanotificationinastaticmethod.);25. 26. 这个类里面包含4个本地方法,一个静态初始化块加载将要生成的dll文件。剩下的方法都是很普通的java方法,等会在backend中回调这些方法。这个类需要一个名为Order的JavaBean。Java代码1. packagecom.chnic.bean;2. 3. publicclassOrder4. 5. privateStringname=Fruit;6. privatedoubleprice;7. privateintamount=30;8. 9. publicOrder()10. 11. 12. 13. publicintgetAmount()14. returnamount;15. 16. 17. publicvoidsetAmount(intamount)18. this.amount=amount;19. 20. 21. publicStringgetName()22. returnname;23. 24. 25. publicvoidsetName(Stringname)26. =name;27. 28. 29. publicdoublegetPrice()30. returnprice;31. 32. 33. publicvoidsetPrice(doubleprice)34. this.price=price;35. 36. javaview plaincopy1. packagecom.chnic.bean;2. 3. publicclassOrder4. 5. privateStringname=Fruit;6. privatedoubleprice;7. privateintamount=30;8. 9. publicOrder()10. 11. 12. 13. publicintgetAmount()14. returnamount;15. 16. 17. publicvoidsetAmount(intamount)18. this.amount=amount;19. 20. 21. publicStringgetName()22. returnname;23. 24. 25. publicvoidsetName(Stringname)26. =name;27. 28. 29. publicdoublegetPrice()30. returnprice;31. 32. 33. publicvoidsetPrice(doubleprice)34. this.price=price;35. 36. JavaBean中,我们为两个私有属性赋值,方便后面的例子演示。到此为止除了测试代码之外的Java端的代码就全部高调了,接下来进行生成.h 头文件、建立C+工程的工作,在这里就一笔带过,不熟悉的朋友请回头看第一篇。在工程里我们新建一个名为Foctory的C+ source file 文件,去实现那些native方法。具体的代码如下。Cpp代码1. #include2. #include3. #includecom_chnic_service_Business.h4. 5. jobjectgetInstance(JNIEnv*env,jclassobj_class);6. 7. JNIEXPORTjdoubleJNICALLJava_com_chnic_service_Business_getPrice(JNIEnv*env,8. jobjectobj,9. jstringname)10. 11. constchar*pname=env-GetStringUTFChars(name,NULL);12. coutBeforerelease:pnameReleaseStringUTFChars(name,pname);17. coutAfterrelease:pnameReleaseStringUTFChars(name,pname);23. coutAfterrelease:pnameFindClass(com/chnic/bean/Order);35. jobjectorder=getInstance(env,order_class);36. 37. jmethodIDsetName_method=env-GetMethodID(order_class,setName,(Ljava/lang/String;)V);38. env-CallVoidMethod(order,setName_method,name);39. 40. jmethodIDsetAmount_method=env-GetMethodID(order_class,setAmount,(I)V);41. env-CallVoidMethod(order,setAmount_method,amount);42. 43. returnorder;44. 45. 46. JNIEXPORTjobjectJNICALLJava_com_chnic_service_Business_getRamdomOrder(JNIEnv*env,47. jobjectobj)48. 49. jclassbusiness_class=env-GetObjectClass(obj);50. jobjectbusiness_obj=getInstance(env,business_class);51. 52. jmethodIDnotification_method=env-GetMethodID(business_class,notification,()V);53. env-CallVoidMethod(obj,notification_method);54. 55. jclassorder_class=env-FindClass(com/chnic/bean/Order);56. jobjectorder=getInstance(env,order_class);57. jfieldIDamount_field=env-GetFieldID(order_class,amount,I);58. jintamount=env-GetIntField(order,amount_field);59. coutamount:amountGetObjectClass(obj);69. jmethodIDgetName_method=env-GetMethodID(order_class,getName,()Ljava/lang/String;);70. jstringname_str=static_cast(env-CallObjectMethod(obj,getName_method);71. constchar*pname=env-GetStringUTFChars(name_str,NULL);72. 73. coutNameinJava_com_chnic_service_Business_analyzeOrder:pnameGetStaticMethodID(cls,notificationByStatic,()V);75. env-CallStaticVoidMethod(cls,notification_method_static);76. 77. 78. 79. jobjectgetInstance(JNIEnv*env,jclassobj_class)80. 81. jmethodIDconstruction_id=env-GetMethodID(obj_class,()V);82. jobjectobj=env-NewObject(obj_class,construction_id);83. returnobj;84. cppview plaincopy1. #include2. #include3. #includecom_chnic_service_Business.h4. 5. jobjectgetInstance(JNIEnv*env,jclassobj_class);6. 7. JNIEXPORTjdoubleJNICALLJava_com_chnic_service_Business_getPrice(JNIEnv*env,8. jobjectobj,9. jstringname)10. 11. constchar*pname=env-GetStringUTFChars(name,NULL);12. coutBeforerelease:pnameReleaseStringUTFChars(name,pname);17. coutAfterrelease:pnameReleaseStringUTFChars(name,pname);23. coutAfterrelease:pnameFindClass(com/chnic/bean/Order);35. jobjectorder=getInstance(env,order_class);36. 37. jmethodIDsetName_method=env-GetMethodID(order_class,setName,(Ljava/lang/String;)V);38. env-CallVoidMethod(order,setName_method,name);39. 40. jmethodIDsetAmount_method=env-GetMethodID(order_class,setAmount,(I)V);41. env-CallVoidMethod(order,setAmount_method,amount);42. 43. returnorder;44. 45. 46. JNIEXPORTjobjectJNICALLJava_com_chnic_service_Business_getRamdomOrder(JNIEnv*env,47. jobjectobj)48. 49. jclassbusiness_class=env-GetObjectClass(obj);50. jobjectbusiness_obj=getInstance(env,business_class);51. 52. jmethodIDnotification_method=env-GetMethodID(business_class,notification,()V);53. env-CallVoidMethod(obj,notification_method);54. 55. jclassorder_class=env-FindClass(com/chnic/bean/Order);56. jobjectorder=getInstance(env,order_class);57. jfieldIDamount_field=env-GetFieldID(order_class,amount,I);58. jintamount=env-GetIntField(order,amount_field);59. coutamount:amountGetObjectClass(obj);69. jmethodIDgetName_method=env-GetMethodID(order_class,getName,()Ljava/lang/String;);70. jstringname_str=static_cast(env-CallObjectMethod(obj,getName_method);71. constchar*pname=env-GetStringUTFChars(name_str,NULL);72. 73. coutNameinJava_com_chnic_service_Business_analyzeOrder:pnameGetStaticMethodID(cls,notificationByStatic,()V);75. env-CallStaticVoidMethod(cls,notification_method_static);76. 77. 78. 79. jobjectgetInstance(JNIEnv*env,jclassobj_class)80. 81. jmethodIDconstruction_id=env-GetMethodID(obj_class,()V);82. jobjectobj=env-NewObject(obj_class,construction_id);83. returnobj;84. 可以看到,在我Java中的四个本地方法在这里全部被实现,接下来针对这四个方法来解释下,一些JNI相关的API的使用方法。先从第一个方法讲起吧:1.getPrice(String name)这个方法是从foreground传递一个类型为string的参数到backend,然后backend判断返回相应的价格。在cpp的代码中,我们用GetStringUTFChars这个方法来把传来的j
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025上海市个人住房装修合同样本
- 2025年维修实践试题及答案
- 2025年航空器材维修工程师专业知识评定试题及答案
- 高炉订购合同模板(3篇)
- 高空作业施工免责合同(3篇)
- 主题餐厅店铺门面转让及餐饮服务标准合作协议
- 2025贵族公务员面试题及答案
- 人工智能研发担保还款技术领先保障协议
- 供应链金融定向合作协议范本
- 大连市二手房房产买卖及装修款项结算合同
- 2025秋一年级语文上册-【语文园地八】教学课件
- 2025年商务短信平台软件系统项目市场调查研究报告
- 基于大数据和人工智能的数字化口腔诊疗服务创新研究报告
- (高清版)DG∕TJ 08-2251-2018 消防设施物联网系统技术标准
- 钣金生产安全培训
- 《解剖学课件:人体解剖学概要》
- T∕CACM 1096-2018 中医治未病技术操作规范 熏蒸
- 2024年浦东新区社区工作者招聘笔试真题
- 花艺沙龙合同协议
- 糖尿病酮症酸中毒课件
- 软件测试师面试题及答案
评论
0/150
提交评论