在java中调用c语言——JNI入门

来源:互联网 发布:淘宝信用查询网站 编辑:程序博客网 时间:2024/05/18 13:25


本文章非原创,多数内容参考以下两篇博客,感谢。由于我在实践中遇到一些问题,所以打算做一些补充,重新写一篇更完整的JNI入门文章。

使用JNI进行Java与C/C++语言混合编程(1)--在Java中调用C/C++本地库

Java语言与C语言混合编程(1)--Java native 关键字


一、编写java文件:创建native method

简单地讲,一个 native Method 就是一个java调用非java代码的接口。表示一个方法的实现在java以外,已经在本地实现了。

如果想更加了解 native Method 可以查看 Java语言与C语言混合编程(1)--Java native 关键字


下面我们举个例子。


package myJNI;public class myJNI {    public native int intMethod(int n);    public native boolean booleanMethod(boolean bool);    public native String stringMethod(String text);    public native int intArrayMethod(int[] intArray);         public static void main(String[] args) {        System.loadLibrary("myJNIdll");//注意: 不可以在代码中写上后缀 .dll 或 .so        myJNI sample = new myJNI();        int square = sample.intMethod(5);        boolean bool = sample.booleanMethod(true);        String text = sample.stringMethod("Java");        int sum = sample.intArrayMethod(new int[]{1,2,3,4,5,8,13});                 System.out.println("intMethod:" + square);        System.out.println("booleanMethod:" + bool);        System.out.println("stringMethod:" + text);        System.out.println("intArrayMethod:" + sum);    }}

上面有4个native方法, 4个native方法就是我们需要用C来实现的方法.

分别是4种类型的参数, int, boolean, String, int[].

其中有一句比较重要, 这句话加载了动态类库

System.loadLibrary("myJNI");

在windows下加载的就是myJNI.dll, 在Linux下加载的就是myJNI.so.

本文使用的windows, 所以后面使用myJNI.dll来表示myJNI动态链接库.

编译myJNI.java, 使用命令行(windows是cmd, linux下一般是bash)

javac myJNI.java


可以看到myJNI.class文件


二、使用javah生成头文件

javah myJNI.myJNI

注意带上包名,我们的文件一般有包名。我的例子里包名是myJNI,在Java文件里可以看到。这和java命令一样。平时IDE用多了容易忘记。

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class myJNI_myJNI */#ifndef _Included_myJNI_myJNI#define _Included_myJNI_myJNI#ifdef __cplusplusextern "C" {#endif/* * Class:     myJNI_myJNI * Method:    intMethod * Signature: (I)I */JNIEXPORT jint JNICALL Java_myJNI_myJNI_intMethod  (JNIEnv *, jobject, jint);/* * Class:     myJNI_myJNI * Method:    booleanMethod * Signature: (Z)Z */JNIEXPORT jboolean JNICALL Java_myJNI_myJNI_booleanMethod  (JNIEnv *, jobject, jboolean);/* * Class:     myJNI_myJNI * Method:    stringMethod * Signature: (Ljava/lang/String;)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_myJNI_myJNI_stringMethod  (JNIEnv *, jobject, jstring);/* * Class:     myJNI_myJNI * Method:    intArrayMethod * Signature: ([I)I */JNIEXPORT jint JNICALL Java_myJNI_myJNI_intArrayMethod  (JNIEnv *, jobject, jintArray);#ifdef __cplusplus}#endif#endif

我们可以看到其中有四个函数声明, Java_包名_完整类名_方法名, 完整类名包括了包名。

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

java类型Signature备注booleanZ byteB charC shortS intI longL floatF doubleD voidV objectL用/分割的完整类名例如: Ljava/lang/String表示String类型Array[签名例如: [I表示int数组, [Ljava/lang/String表示String数组Method(参数签名)返回类型签名例如: ([I)I表示参数类型为int数组, 返回int类型的方法

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

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

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

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


三、编辑DLL文件


1.处理头文件之间的联系


使用dev c++创建 DLL project。给其中添加两个文件,分别是

C:\Program Files\Java\jdk1.8.0_101\include\jni.h

C:\Program Files\Java\jdk1.8.0_101\include\win32\jni_md.h

这两个文件在Java安装目录下,通常默认安装在以上路径。



对 jni.h 文件做一些更改,#include <jni_md.h>改为 #include "C:\Program Files\Java\jdk1.8.0_101\include\win32\jni_md.h"

对我们用 javah 生成的头文件的开头部分 include 语句也要用双引号加文件名。自动生成的是如下样子,改为

#include "C:\Program Files\Java\jdk1.8.0_101\include\jni.h"


这两步的操作是因为 #include < > 表示在库函数里查找,但是 jni.h 和 jni_md.h 并不在c语言的库函数里,而是在

Java 的安装目录里。我们把这两个文件放到了我们的 DLL project 里,如果不用双引号将会差找不到。

深入学习的话去了解 include 语句。

include 关系为 dll.h include jni.h , jni.h include jni_md.h


也可以直接用 #include "jni_md.h" 和 #include "jni.h",要把这两个文件和dll.h放在一个目录下。


然后把我们生成的头文件 myJNI_myJNI.h 的内容全部复制粘贴到 dll.h 文件里,粘贴到下图中选中的位置。



为什么不直接用myJNI_myJNI.h 文件呢?因为如果没有上图里的那几句话,dll 文件是无法编译输出的。你也可以把这几句话

放进myJNI_myJNI.h 文件。


2.编写 c 程序

#include "dll.h"#include <string.h>JNIEXPORT jint JNICALL JNICALL Java_myJNI_myJNI_intMethod  (JNIEnv *env, jobject obj, jint num){    return num * num;} JNIEXPORT jboolean JNICALL JNICALL Java_myJNI_myJNI_booleanMethod  (JNIEnv *env, jobject obj, jboolean boolean){    return !boolean;} JNIEXPORT jstring JNICALL JNICALL Java_myJNI_myJNI_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 JNICALL Java_myJNI_myJNI_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中的大部分函数名都是这样.

之后编译就好了。如果编译不出问题就OK了。


四、运行文件



1 0
原创粉丝点击