由于JNI调用C和调用C++差不多,而且C++中可以混合写C代码,所以这里主要是写关于JNI调用C++的部分。
一般步骤:
- 先是写普通的Java类,其中包括本地方法调用。
- 然后编译这个Java类,调用javah命令,生成.h头文件
- 接着,就是实现头文件中的函数;实现过程中有点比较麻烦,要知道JNI中JAVA和C/C++的类型转换,比如数组类型的转换,基本类型的转换等,最好是看文档,或者网上找相关资料。
JNI的调用效果图:
下面是调用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 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的路径有问题,注意这个路径是安装路径。用命令设置下,如果设置还是报这个错误,那么就到环境变量那里修改。
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