JNI快速入门


为Java Native Interface 即Java本地接口,使用此种机制可以实现Java和C/C++互相调用.本文对该机制进行快速入门,

并记录了我在整个过程中遇到的问题及解决方法。

Java调用C++代码

Java调用C++代码本质上是对C++生成的动态库进行调用而不是直接对C/C++代码进行调用。

端接口;


1. public class HelloJNI {  
2.   
3. /**
4.      * @param args
5.          Native关键字
6.      */  
7. private native void printMsg();  
8.        
9. public static void main(String[] args) {  
10. // TODO Auto-generated method stub  
11. new HelloJNI().printMsg();  
12.     }  
13.   
14. static {  
15. //静态代码块中的代码加载dll,保证该类的dll文件只被加载一次  
16. "HelloJNI");  
17.             
18.      }  
19. }



javac HelloWorld.java

javah工具生成C++接口文件.

生成文件HelloJNI.h文件


1. /* DO NOT EDIT THIS FILE - it is machine generated */  
2. #include <jni.h>  
3. /* Header for class HelloJNI */  
4.   
5. #ifndef _Included_HelloJNI  
6. #define _Included_HelloJNI  
7. #ifdef __cplusplus  
8. extern "C" {  
9. #endif  
10. /*
11.  * Class:     HelloJNI
12.  * Method:    printMsg
13.  * Signature: ()V   
14. 方法签名:用来映射C++function Java_HelloJNI_printMsg和Java function  printMsg的参数和返回值
15. 
16. 可以使用命令:[ javap -s -private 类名]   来查看方法签名
17. 
18. 参数说明:
19. JNIEnv* :JNI环境指针.该指针为JVM当前中当前线程的句柄,包含一些映射信息和管理信息
20. Jobject :调用该本地方法的方法的引用,如果调用方法(本地方法调用者)是静态方法,该参数将是jclass类型
21. 
22.  */  
23. JNIEXPORT void JNICALL Java_HelloJNI_printMsg  
24.   (JNIEnv *, jobject);  
25.   
26. #ifdef __cplusplus  
27. }  
28. #endif  
29. #endif


参数说明:

:JNI环境指针.该指针为JVM当前中当前线程的句柄,包含一些映射信息和管理信息

:调用该本地方法的方法的引用,如果调用方法(本地方法调用者)是静态方法,该参数将是jclass类型

:

HelloJNI的方法签名,其意义见下表:



JNI快速入门_java



类型签名的意义:


JNI快速入门_Java_02

HelloJNI.h头文件创建动态链接库。具体步骤为:

新建VC6.0动态链接库工程HelloJNI

将HelloJNI.h头文件copy到工程目录下,这是比较偷懒的做法,比较好的做法是创建一个文件来存放自己外部的头文件,

在项目中指定路径projetct->setting->C++->processor->additional include directoryes:

在工程中HelloJNI.cpp文件中导入必须的头文件

#include "jni.h"

#include "HelloJNI.h"

头文件在jdk安装目录下的include文件夹,如果找不到,使用2中的方式包含该路径。

,HelloJNI.h的实现文件为HelloJNI.cpp 


1. #include "jni.h"  //这里不要用#include <jin.h>  
2. #include "HelloJNI.h"  
3.   
4. BOOL APIENTRY DllMain( HANDLE hModule,   
5. DWORD  ul_reason_for_call,   
6. LPVOID lpReserved  
7.                      )  
8. {  
9. return TRUE;  
10. }  
11.   
12.   
13. JNIEXPORT void JNICALL Java_HelloJNI_printMsg (JNIEnv * env, jobject obj){  
14. //简单的测试语句,打印jdk版本,打印出的是一个Long值  
15.         jint versionid = env->GetVersion();  
16.   
17. "jdk version:%d",versionid);  
18.   
19. }


HelloJNI工程,生成HelloJNI.dll动态链接库文件,生成的dll文件会在debug文件夹或者release文件夹中;

HelloJNI.dll文件copy到和HelloJNI.class同一目录下

java HelloJNI



JNI快速入门_java_03


C++调用Java代码



调用Java代码的原理是在C++代码中创建JVM,加载class文件然后执行。

下面来简单入门。





HelloCPP.java文件.



1. class HelloCPP{  
2.   
3. public void printHello(){  
4. "hello C++");  
5. }  
6. }  
 
  
 
package).
javac HelloCPP.java 
HelloCPP.class
 
VC6.0创建Win32 console工程CppCallJavaDemo

1. // CppCallJavaDemo.cpp : Defines the entry point for the console application.  
2. //  
3. #include "jni.h"  
4.   
5. // 环境变量PATH在windows下和linux下的分割符定义  
6. #ifdef _WIN32  
7. #define PATH_SEPARATOR ';'  
8. #else  
9. #define PATH_SEPARATOR ':'  
10. #endif  
11.   
12.   
13. int main(int argc, char* argv[])  
14. {  
15.     JavaVMOption options[1];  
16.     JNIEnv *env;  
17.     JavaVM *jvm;  
18.     JavaVMInitArgs vm_args;  
19.   
20. long status;  
21.     jclass jcls;  
22.     jmethodID mid;  
23.   
24.     jobject obj;  
25.   
26.   
27. "-Djava.class.path=.";    
28. //这里声明类的寻找路径,可以有过个路径    
29. //"."表示工程工程目录  
30. //  options[1].optionString = "-Djava.class.path=H:\\project\\CPPCallJava";    
31. //  memset(&vm_args,0,sizeof(vm_args));  
32.     vm_args.version=JNI_VERSION_1_6;  
33.     vm_args.nOptions=1;  
34.     vm_args.options = options;  
35.   
36. //获得虚拟机,从返回值判断是否成功 我在测试过程中遇到的返回参数是 0 -1 -3  
37. /*
38.     #define JNI_OK           0              success     成功
39.     #define JNI_ERR          (-1)           unknown error   未知错误,返回这个参数就难以判断问题所在了
40.     #define JNI_EDETACHED    (-2)           thread detached from the VM    JVM的派生线程
41.     #define JNI_EVERSION     (-3)           JNI version error    JNI版本不一致,返回这个参数就需要判断jvm与JNI是否版本一致
42.     #define JNI_ENOMEM       (-4)           not enough memory    内存不够
43.     #define JNI_EEXIST       (-5)           VM already created   已经创建了JVM
44.     #define JNI_EINVAL       (-6)           invalid arguments    参数不正确
45.     */  
46. void**)&env, &vm_args);  
47.   
48. if(!env){  
49. "env is null!\r\n");  
50.     }  
51.           
52. if(status != JNI_ERR){  
53.   
54. //根据类名找到类  
55. "HelloCPP");  
56. //类加了package需要这样查找,包名以"/"分开  
57. //  jcls =    env->FindClass("test/HelloCPP");  
58.   
59. if(jcls != NULL){   
60.   
61. //获得methodid 参数分别为 jclass 调用方法名 方法签名 signature   
62. //使用javap -s -private 类名  查看方法签名  
63. "printHello","()V");           
64.   
65.   
66. //创建对象  
67. //      obj = env->AllocObject(jcls);  
68.   
69. //获得默认构造方法的id  
70. "<init>","()V");  
71. //根据默认构造方法创建对象  
72.             obj = env->NewObject(jcls,constid);  
73.   
74. "method id:%d\r\n",mid);  
75.               
76. if(mid != 0){  
77.                   
78.                 env->CallVoidMethod(obj,mid);  
79.             }  
80. "get object!!\r\n");  
81. else{  
82. "not get object!!\r\n");  
83.         }  
84.     }  
85.     jvm->DestroyJavaVM();  
86. return 0;  
87. }

运行结果:



JNI快速入门_#define_04




好了,要是这些代码跑起来还有一些工作要做!

jin.h文件路径

jdk安装目录下的Include包中 

jin.Lib文件路径,该路径在jdk安装目录下的lib包中

Project->setting->link->input->additional library path:

jvm.dll文件了,在jre/bin/server/目录下可以找到它,把它配到环境变量中。

,这样就可以运行exe了!

在整个过程中遇到了两个问题。

:创建JVM失败,无赖的是返回的Statue是-1,表示是未知错误,多方检查才发现是JVM.dll用错了,刚开始配的是/jre/bin/client目录下的jvm.dll,

改正之后成功创建JVM.

找不到类。

这个问题困扰了我好一阵子。

首先是一遍遍检查了配置,没问题。依然找不到类。直觉告诉我肯定是类的路径不对。

HelloCPP.class文件置于各个目录下,依然找不到了。

(package test;刚开始是有这个马甲的)去掉了,把class文件放到工程目录下,bingo!!

如果给类加了马甲,那么在工程目录下需要创建实体目录才行。

packate test;

test,然后将class文件置于其中,也可以找到类。


到此做了简单入门。网上有一些参考资料,这里可以下载本文中的演示例子以及一些JNI相关资料:demo下载