Java JNI 初探
什么是 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. |