什么是 JNI ?

JNI(Java Native Interface),Java 本地接口,是一个本机编程接口,提供了Java与C,C++语言的通信。JNI的主要实现方式为在Java中通过System.loadLibrary来加载C/C++编写的DLL。以此来调用C/C++实现的函数。

Java调用C++

Java语言的写法:

package com.sighingnow.maker;

/**
 * @author Tao He
 * 
 */
public class Maker {

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

    @SuppressWarnings("javadoc")
    public native int DisplayMakerInfo(String info);

    @SuppressWarnings("javadoc")
    public static void main(String[] args) {
        int length = (new Maker()).DisplayMakerInfo("Hello Maker JNI !");
        System.out.println(length);
    }
}

native关键字定义的方法用C/C++实现。

通过JDK中的javah工具来生成C/C++的头文件,在这之前,需要先编译java文件。

javac com/sighingnow/maker/Maker.java

然后,用如下命令生成头文件:

javah com.sighingnow.maker.Maker

得到文件名为com_sighingnow_maker_Maker.h的头文件,头文件的内容如下所示:

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

#ifndef _Included_com_sighingnow_maker_Maker
#define _Included_com_sighingnow_maker_Maker
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_sighingnow_maker_Maker
 * Method:    DisplayMakerInfo
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_sighingnow_maker_Maker_DisplayMakerInfo
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

接下来创建一个同名的C++源文件来实现该头文件中定义的函数。

内容为:

#include <iostream>
#include <cstring>

#include "com_sighingnow_maker_Maker.h"

using namespace std;

/**
 * author: Tao He
 */

JNIEXPORT jint JNICALL Java_com_sighingnow_maker_Maker_DisplayMakerInfo
  (JNIEnv *env, jobject object, jstring info) 
{
    cout << "JNI method called!" << endl;

    const char *str = env->GetStringUTFChars(info, 0);
    cout << "info: " << str << endl;

    return strlen(str);
}

/* vim: set ts=4, sw = 4 */

使用MinGW编译C++文件,得到动态链接库。编译命令为:

g++ -shared -ID:/Java/jdk1.8.0_25/include -ID:/Java/jdk1.8.0_25/include/win32 -Wl,--add-stdcall-alias com_sighingnow_maker_Maker.cpp -o Maker.dll

编译成功后,会在当前目录得到一个名为”Maker.dll”的动态链接库。然后,使用如下命令运行:

java com.sighingnow.maker.maker

得到如下输出:

JNI method called!
info: Hello Maker JNI!
17

说明Java字节码在运行时成功载入了动态链接库并成功运行了其中的函数。

在编译时一定要注意加上-Wl,--add-stdcall-alias选项(MinGW环境),否则,可能会出现调用dll中的函数时找不到函数的错误。

--add-stdcall-alias

If given, symbols with a stdcall suffix (@nn) will be exported as-is and also with the suffix stripped. [This option is specific to the i386 PE targeted port of the linker]

运行时如果出现找不到dll的错误,可以通过制定java.library.path来解决。通过如下Java语句可以得到java.library.path的值:

System.getProperty("java.library.path");

JNI的数据类型

JNI中定义的数据类型如下表所示:

Type Description
jboolean Holds a Java programming language boolean. Unsigned 8 bits.
jint Holds a Java programming language int. Signed 32 bits.
jlong Holds a Java programming language long. Signed 64 bits.
jfloat Holds a Java programming language float. 32 bits.
jdouble Holds a Java programming language double. 64 bits.
jobject Holds a Java programming language object.
jclass Holds a Java programming language class.
jvalue Is a union of all primitive types and jobject. Thus, holds any Java programming language value.
jfieldID Identifies a Java programming language field. jfieldIDs returned by JVM TI functions and events may be safely stored.
jmethodID Identifies a Java programming language method, initializer, or constructor. jmethodIDs returned by JVM TI functions and events may be safely stored. However, if the class is unloaded, they become invalid and must not be used.
JNIEnv Pointer to the JNI function table. Pointer to this (JNIEnv *) is a JNI environment.