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的方法签名,其意义见下表:
类型签名的意义:
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
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. }
运行结果:
好了,要是这些代码跑起来还有一些工作要做!
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下载