Keep it simple.

通过Java Native Interface(JNI)我们可以很容易的使用Java语言调用C/C++编写的本机代码,本文将创建一个简单的JNI调用示例来介绍其实现步骤。

实现JNI的大致步骤如下:
1、创建Java类,并定义native方法;
2、使用javah命令生成.h头文件;
3、编写C/C++代码,实现.h头文件中定义的方法;
4、将C/C++代码编译为.dll文件;
5、在Java中调用native方法。

1、创建Java类,并定义native方法

打开Eclipse并创建一个Java项目,新建JniFunctions.java类,代码如下:

package com.zhengqiangblog.test.jni;

public class JniFunctions {

    public native String function1();

    public native String function2();

}

JniFunctions.java类中定义了两个方法function1()function2(),并使用native关键字修饰,表示这是原生方法。原生方法无需编写方法体,只需定义方法签名即可,其具体实现是在C/C++中。

2、使用javah命令生成.h头文件

Eclipse会自动编译JniFunctions.java类,编译后的文件位于${workspace}\jni_test\bin目录中。打开命令提示符cmd,切换到该目录。

生成C/C++的.h头文件,在命令提示符中使用下面的命令:

javah com.zhengqiangblog.test.jni.JniFunctions

执行成功后,会在${workspace}\jni_test\bin\目录下生成com_zhengqiangblog_test_jni_JniFunctions.h文件,该文件的内容如下所示:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_zhengqiangblog_test_jni_JniFunctions */

#ifndef _Included_com_zhengqiangblog_test_jni_JniFunctions
#define _Included_com_zhengqiangblog_test_jni_JniFunctions
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_zhengqiangblog_test_jni_JniFunctions
 * Method:    function1
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_zhengqiangblog_test_jni_JniFunctions_function1
  (JNIEnv *, jobject);

/*
 * Class:     com_zhengqiangblog_test_jni_JniFunctions
 * Method:    function2
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_zhengqiangblog_test_jni_JniFunctions_function2
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

这个.h头文件定义了两个方法,这两个方法与Java文件中的两个native方法是一一对应的,接下来我们就要实现这两个方法。

3、编写C/C++代码,实现.h头文件中定义的方法

创建JniFunctions.c文件,并编写如下代码:

#include <stdio.h>
#include <jni.h>
#include "com_zhengqiangblog_test_jni_JniFunctions.h"

JNIEXPORT jstring JNICALL Java_com_zhengqiangblog_test_jni_JniFunctions_function1
  (JNIEnv *env, jobject jobj)
{
    printf("Hello world!\n");
}


JNIEXPORT jstring JNICALL Java_com_zhengqiangblog_test_jni_JniFunctions_function2
  (JNIEnv *env, jobject jobj)
{
    printf("Hello JNI!\n");
}

第1行引入了C标准IO头文件stdio.h;第2行引入了JNI头文件jni.h,该头文件位于%JAVA_HOME%\include目录中;第3行引入的就是我们使用javah生成的com_zhengqiangblog_test_jni_JniFunctions.h头文件,下面的两个方法就是对头文件中定义的两个方法的具体实现。

4、将C/C++代码编译为.dll文件

使用64位的GCC将上面的C源码编译为Windows动态链接库(.dll)文件,此处使用mingw-w64编译。

x86_64-w64-mingw32-gcc.exe -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -Wl,--kill-at -s -o JniFunctions.dll JniFunctions.c

参数说明:
-I"%JAVA_HOME%\include"-I"%JAVA_HOME%\include\win32" 将 JNI 的头文件引入进来,不引入则无法通过编译;
-shared 生成库文件;
-s 删除符号表,减小文件体积;
-o 指定成生的文件名;
-Wl,--kill-at 如果不加该参数,则函数名后会添加“@n”形式的后缀。

5、在Java中调用native方法

将编译好的JniFunctions.dll文件复制到eclipse项目的resources目录中,如下图所示:

并将JniFunctions.dll文件配置到JRE的Native library中,使JRE能够加载它,右键点击项目,选择“Properties”,并按下图所示配置:

编写Main.java类,实现main函数,测试native方法调用:

package com.zhengqiangblog.test.jni;

public class Main {

    static {
        System.loadLibrary("JniFunctions");
    }

    public static void main(String[] args) {
        JniFunctions functions = new JniFunctions();
        functions.function1();
        functions.function2();
    }

}

运行该文件,看到控制台输出如下,表明native方法被调用成功:

Hello world!
Hello JNI!
  • zhengqiang
  • 技术
  • 2016-10-16 11:03:23.0
  • 504