我们可以看到其中有四个函数声明, Java_完整类名_方法名, 完整类名包括了包名, 例如demo.Sample1是完整类名, 对应的这里就是demo_Sample1.

在注释中我们可以看到这样一个东西 Signature, 这个是方法的签名. 关于Signature, 下面通过一个表格来说明.


java类型



Signature



备注



boolean



Z




byte



B




char



C




short



S




int



I




long



L




float



F




double



D




void



V




object



L用/分割的完整类名



例如: Ljava/lang/String表示String类型



Array



[签名



例如: [I表示int数组, [Ljava/lang/String表示String数组



Method



(参数签名)返回类型签名



例如: ([I)I表示参数类型为int数组, 返回int类型的方法


上面头文件的第一个函数声明

JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *, jobject, jint);

注释中的签名是 Signature: (I)I

在每个函数的参数列表中都有JNIEnv *和 jobject两个参数, 这两个参数稍候说明.

实现头文件中的函数

可以使用C语言来实现, 也可以使用C++来实现, 下面先说说C语言的实现.


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39



#include "Sample1.h"

#include <string.h>


JNIEXPORT jint JNICALL Java_Sample1_intMethod

  (JNIEnv *env, jobject obj, jint num)

{

    return num * num;

}


JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod

  (JNIEnv *env, jobject obj, jboolean   boolean)

{

    return !boolean;

}


JNIEXPORT jstring JNICALL Java_Sample1_stringMethod

  (JNIEnv *env, jobject obj, jstring string)

{

    const char* str = (*env)->GetStringUTFChars(env,   string, 0);

    char cap[128];

    strcpy(cap, str);

    (*env)->ReleaseStringUTFChars(env,   string, 0);

    return (*env)->NewStringUTF(env,   strupr(cap));

}


JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod

  (JNIEnv *env, jobject obj, jintArray array)

{

    int i, sum = 0;

    jsize len =   (*env)->GetArrayLength(env, array);

    jint *body =   (*env)->GetIntArrayElements(env, array, 0);


    for (i = 0; i < len;   ++i)

    {

        sum +=   body[i];

    }

    (*env)->ReleaseIntArrayElements(env,   array, body, 0);

    return sum;

}


(*env)->GetStringUTFChars()这个方法, 是用来在Java和C之间转换字符串的, 因为Java本身都使用了双字节的字符, 而C语言本身都是单字节的字符, 所以需要进行转换.

JNIEnv *是每个函数都有的参数, 它包含了很多有用的方法, 使用起来类似Java的反射, 也提供了这样一个编码转换的函数.

GetStringUTFChars()和NewStringUTF(), 第一个是从UTF8转换为C的编码格式, 第二个是根据C的字符串返回一个UTF8字符串.

ReleaseStringUTFChars()是用来释放对象的, 在Java中有虚拟机进行垃圾回收, 但是在C语言中, 这些对象必须手动回收. 否则可能造成内存泄漏.

函数的名字一眼看到就可以猜出功能, jni.h中的大部分函数名都是这样.

如果是C++的话, 这段代码该怎么写?

下面是C++的代码


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39



#include "Sample1.h"

#include <string.h>


JNIEXPORT jint JNICALL Java_Sample1_intMethod

  (JNIEnv *env, jobject obj, jint num)

{

    return num * num;

}


JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod

  (JNIEnv *env, jobject obj, jboolean   boolean)

{

    return !boolean;

}


JNIEXPORT jstring JNICALL Java_Sample1_stringMethod

  (JNIEnv *env, jobject obj, jstring string)

{

    const char* str = env->GetStringUTFChars(string,   0);

    char cap[128];

    strcpy(cap, str);

    env->ReleaseStringUTFChars(string,   0);

    return env->NewStringUTF(strupr(cap));

}


JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod

  (JNIEnv *env, jobject obj, jintArray array)

{

    int i, sum = 0;

    jsize len =   env->GetArrayLength(array);

    jint *body =   env->GetIntArrayElements(array, 0);


    for (i = 0; i < len;   ++i)

    {

        sum +=   body[i];

    }

    env->ReleaseIntArrayElements(array,   body, 0);

    return sum;

}



上述两端代码非常相似, 只有一个不同点

C代码: (*env)->GetStringUTFChars(env, string, 0);

C++代码: env->GetStringUTFChars(string, 0);

C语言中使用的是结构体的函数指针, 而在C++中使用的还是struct, 我们知道struct在C++中和class的功能是几乎一样的, struct也可以用来定义类, 所以env在C++中是个类对象的指针.

编译和运行

这里使用的是微软编译器, 编译C语言版的dll

>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.c -FeSample1.dll

编译C++版本的dll

>cl -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -LD Sample1.cpp -FeSample1.dll

运行

>java Sample1

注意: 64位版本的JDK可能会在运行时报错:

java.lang.UnsatisfiedLinkError: ...Sample1.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform

如果您有这样的错误, 请使用32位的JDK来重新运行.

运行结果如下:


intMethod: 25

booleanMethod: false

stringMethod: JAVA

intArrayMethod: 36


 ​

运行其中的build&run.bat文件即可, 如有错误请根据实际情况修改其中的一些参数.

 ​

参考文献:


  1. Scott Stricker, 用 JNI 进行 Java 编程,      http://www.ibm.com/developerworks/cn/education/java/j-jni/section2.html
  2. JDK 6u30 docs, Java Native      Interface Specification, Chapter 3 JNI Types and Data Structures, Type      Signatures.