由于JNI调用C和调用C++差不多,而且C++中可以混合写C代码,所以这里主要是写关于JNI调用C++的部分。

 一般步骤:

  1. 先是写普通的Java类,其中包括本地方法调用。 
  2. 然后编译这个Java类,调用javah命令,生成.h头文件
  3. 接着,就是实现头文件中的函数;实现过程中有点比较麻烦,要知道JNI中JAVA和C/C++的类型转换,比如数组类型的转换,基本类型的转换等,最好是看文档,或者网上找相关资料。

 

    源代码链接:

 

JNI的调用效果图:

 

jni中调用java java jni调用过程_runtime

 

下面是调用JNI的具体过程:

 

      1.  普通Java类(包含测试方法):MyJNI.java



1 public class MyJNI {
 2 
 3     //加载动态链接库
 4     static {
 5         System.out.println("开始加载动态链接库");
 6         System.loadLibrary("MyJNI");
 7         System.out.println("动态链接库加载完毕。");
 8     }
 9     
10     public native void go();
11     
12     public native void run();
13     
14     public native String getName();
15     
16     public native int[] sort(int[] array);
17     
18     //测试
19     public static void main(String[] args) {
20         MyJNI jni = new MyJNI();
21         int[] array = {5, 3, 6, 35, 74, 8}, sortedArray;
22         
23         jni.run();
24         jni.go();
25         jni.getName();
26         sortedArray = jni.sort(array);
27         //由于这是本地方法调用,这里的数组和平常的数组的引用不太一样。
28         
29         for(int i=0; i<sortedArray.length; i++){
30             System.out.print(sortedArray[i] + "\t");
31         }
32             
33     }
34 }



 

        2.  由Java类编译后生成的C++头文件:MyJNI.h



1 /* DO NOT EDIT THIS FILE - it is machine generated */
 2 #include <jni.h>
 3 /* Header for class MyJNI */
 4 
 5 #ifndef _Included_MyJNI
 6 #define _Included_MyJNI
 7 #ifdef __cplusplus
 8 extern "C" {
 9 #endif
10 /*
11  * Class:     MyJNI
12  * Method:    go
13  * Signature: ()V
14  */
15 JNIEXPORT void JNICALL Java_MyJNI_go
16   (JNIEnv *, jobject);
17 
18 /*
19  * Class:     MyJNI
20  * Method:    run
21  * Signature: ()V
22  */
23 JNIEXPORT void JNICALL Java_MyJNI_run
24   (JNIEnv *, jobject);
25 
26 /*
27  * Class:     MyJNI
28  * Method:    getName
29  * Signature: ()Ljava/lang/String;
30  */
31 JNIEXPORT jstring JNICALL Java_MyJNI_getName
32   (JNIEnv *, jobject);
33 
34 /*
35  * Class:     MyJNI
36  * Method:    sort
37  * Signature: ([I)[I
38  */
39 JNIEXPORT jintArray JNICALL Java_MyJNI_sort
40   (JNIEnv *, jobject, jintArray);
41 
42 #ifdef __cplusplus
43 }
44 #endif
45 #endif



 

       3.  需要调用的C++函数的相关文件:MyJNIImpl.cpp



1 #include <jni.h>
 2 #include "MyJNI.h"
 3 #include <stdio.h>
 4 
 5 
 6 /*
 7  * Class:     MyJNI
 8  * Method:    go
 9  * Signature: ()V
10  */
11 JNIEXPORT void JNICALL Java_MyJNI_go
12   (JNIEnv * env, jobject jobj){
13 
14     printf("I am going....\n");
15 }
16 
17 /*
18  * Class:     MyJNI
19  * Method:    run
20  * Signature: ()V
21  */
22 JNIEXPORT void JNICALL Java_MyJNI_run
23   (JNIEnv * env, jobject jobj){
24 
25     printf("I am running....\n");
26 }
27 
28 /*
29  * Class:     MyJNI
30  * Method:    getName
31  * Signature: ()Ljava/lang/String;
32  */
33 JNIEXPORT jstring JNICALL Java_MyJNI_getName
34   (JNIEnv * env, jobject job){
35 
36    printf("I am GDUTtiantian, go with me.\n");
37    //将字符串转化为jstring类型
38    //jstring就是对应java的String类型
39    jstring p = env->NewStringUTF("GDUTtiantian");
40    return p;
41 }
42 
43 /*
44  * Class:     MyJNI
45  * Method:    sort
46  * Signature: ([I)[I
47  */
48 JNIEXPORT jintArray JNICALL Java_MyJNI_sort
49   (JNIEnv * env, jobject jobj, jintArray array){
50 
51 jint* arr;//定义一个整形指针
52     int sum=0;
53     //对于整形数组的处理,主要有GetIntArrayElements与GetIntArrayRegion
54     //第一种方法
55     arr = env->GetIntArrayElements(array, NULL);//得到一个指向原始数据类型内容的指针
56     jint length = env->GetArrayLength(array);//得到数组的长度
57 
58     for(int i=0; i<length; i++){
59         for(int j=i+1; j<length; j++){
60             if(arr[i] > arr[j]){
61                 jint temp = arr[i];
62                 arr[i] = arr[j];
63                 arr[j] = temp;
64             }
65         }
66     }
67 
68 
69     for(int i=0; i<length; i++){
70         printf("%d ", arr[i]);
71     }
72 
73     printf("\n排序完成\n");
74 
75     jintArray javaArray = env->NewIntArray(length);
76     env->SetIntArrayRegion(javaArray, 0, length, arr);
77 
78     return javaArray;//返回排序后的数组
79 }



 

 

编译之后,生成一个动态链接库文件:MyJNI.dll

在Java类中就是通过加载这个库文件,调用其中的相关函数。

调用的相关命令:

  • javac *.java
  • javah MyJNI
  • set JAVA_HOME=D:\SoftwareDeveloping\jdk32bit_1.6
  • g++ -Wl,--add-stdcall-alias -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o MyJNI.dll MyJNIImpl.cpp
  • java MyJNI

 

在操作过程中可能会出现的异常:

  1.  第一个异常 
1 C:\Users\Administrator\Desktop>java HelloJNI
 2 Exception in thread "main" java.lang.UnsupportedClassVersionError: HelloJNI : Unsupported major.minor version 51.0
 3         at java.lang.ClassLoader.defineClass1(Native Method)
 4         at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
 5         at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
 6         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
 7         at java.net.URLClassLoader.defineClass(URLClassLoader.java:283)
 8         at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
 9         at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
10         at java.security.AccessController.doPrivileged(Native Method)
11         at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
12         at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
13         at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
14         at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
15 Could not find the main class: HelloJNI.  Program will exit.



这个是JAVA虚拟机的版本低于编译器的问题,如果你用一个编译编译之后,然后把.class文件移动到另一个环境下执行,可能会出现这个问题。

 

  2.     第二个异常



1 C:\Users\Administrator\Desktop>javac *.java
 2 
 3 C:\Users\Administrator\Desktop>java HelloJNI
 4 Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\Users\Administrator\Desktop\hello.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
 5         at java.lang.ClassLoader$NativeLibrary.load(Native Method)
 6         at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1807)
 7         at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1732)
 8         at java.lang.Runtime.loadLibrary0(Runtime.java:823)
 9         at java.lang.System.loadLibrary(System.java:1028)
10         at HelloJNI.<clinit>(HelloJNI.java:3)
11 Could not find the main class: HelloJNI.  Program will exit.



动态链接库.dll是32位,而JVM是64位,不匹配;可以安装一个32位的JVM;或者在64位环境下重新编译一个新的.dll文件。

    3.    第三个异常



1 C:\Users\Administrator\Desktop\jni>g++ -Wl,--add-stdcall-alias -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o MyJNI.dll MyJNIImpl.cpp
 2 In file included from MyJNIImpl.cpp:1:
 3 MyJNI.h:2:17: jni.h: No such file or directory
 4 In file included from MyJNIImpl.cpp:1:
 5 MyJNI.h:15: error: expected constructor, destructor, or type conversion before "void"
 6 MyJNI.h:15: error: expected `,' or `;' before "void"
 7 MyJNI.h:23: error: expected constructor, destructor, or type conversion before "void"
 8 MyJNI.h:23: error: expected `,' or `;' before "void"
 9 MyJNI.h:31: error: `JNIEXPORT' does not name a type
10 MyJNI.h:39: error: `JNIEXPORT' does not name a type
11 MyJNIImpl.cpp:10: error: expected constructor, destructor, or type conversion before "void"
12 MyJNIImpl.cpp:10: error: expected `,' or `;' before "void"
13 MyJNIImpl.cpp:21: error: expected constructor, destructor, or type conversion before "void"
14 MyJNIImpl.cpp:21: error: expected `,' or `;' before "void"
15 MyJNIImpl.cpp:32: error: `JNIEXPORT' does not name a type
16 MyJNIImpl.cpp:47: error: `JNIEXPORT' does not name a type



这个报错主要是找不到jni.h文件,一般的原因:JAVA_HOME的路径有问题,注意这个路径是安装路径。用命令设置下,如果设置还是报这个错误,那么就到环境变量那里修改。

 

参考资料:http://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html">http://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html

    

JNI类型转换:

函数

Java 数组类型

本地类型

GetBooleanArrayElements

jbooleanArray

jboolean

GetByteArrayElements

jbyteArray

jbyte

GetCharArrayElements

jcharArray

jchar

GetShortArrayElements

jshortArray

jshort

GetIntArrayElements

jintArray

jint

GetLongArrayElements

jlongArray

jlong

GetFloatArrayElements

jfloatArray

jfloat

GetDoubleArrayElements

jdoubleArray

jdouble