JNI即Java Native Interface(Java本地接口),是一个协议,主要作用为:实现Java调用c/c++代码(类库),或者C/C++调用Java代码。本文主要介绍通过JNI实现java调用C/C++
通过Java jni来调用native code,需要如下几个步骤:
接下来我们看一个例子:
1)java代码:JavaJNI.java
public class JavaJNI {
static{
System.load("/data/javaJNI.so");
}
native void printstr();
native void printstr(String str);
public static void main(String[] args) {
JavaJNI javaJNI = new JavaJNI();
javaJNI.printstr();
javaJNI.printstr("wo shi ben di diao yong ce shi");
}
}
这里声明了两个本地方法接口;同时指定了将来C/C++实现了本地方法接口的动态库路径。
2)编译、导出头文件:
$ pwd
/data/kevinliu/jni
$ javac JavaJNI.java
$ javah JavaJNI
$ ll
-rw-r--r-- 1 root root 521 Oct 12 10:48 JavaJNI.class
-rw-r--r-- 1 root root 557 Oct 12 10:48 JavaJNI.h
-rw-r--r-- 1 root root 324 Oct 12 10:46 JavaJNI.java
注:javah后面跟得是类全路径。这里生成的JavaJNI.h是自动生成的,我们大致看下内容:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class JavaJNI */
#ifndef _Included_JavaJNI
#define _Included_JavaJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: JavaJNI
* Method: printstr
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JavaJNI_printstr__
(JNIEnv *, jobject);
/*
* Class: JavaJNI
* Method: printstr
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_JavaJNI_printstr__Ljava_lang_String_2
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
3)编写C代码,实现对应的方法:JavaJNI.c
#include "JavaJNI.h"
#include
JNIEXPORT void JNICALL Java_JavaJNI_printstr__(JNIEnv *env, jobject obj) {
printf("%s\n","hello jni");
return;
}
JNIEXPORT void JNICALL Java_JavaJNI_printstr__Ljava_lang_String_2(JNIEnv *env, jobject obj, jstring string){
const char *str = (*env)->GetStringUTFChars(env,string,0);
printf("%s\n",str);
}
4)编译c,生成动态链接库:
首先,我们先编一下JavaJNI.c,看是否有语法错误:
$ gcc -c JavaJNI.c
$ ll
-rw-r--r-- 1 root root 353 Oct 12 10:48 JavaJNI.c
-rw-r--r-- 1 root root 521 Oct 12 10:48 JavaJNI.class
-rw-r--r-- 1 root root 557 Oct 12 10:48 JavaJNI.h
-rw-r--r-- 1 root root 324 Oct 12 10:46 JavaJNI.java
-rw-r--r-- 1 root root 1712 Oct 12 10:49 JavaJNI.o
$ rm -rf JavaJNI.o
生成动态链接库:
$ find / -name jni.h
/usr/local/lib/gcc/x86_64-unknown-linux-gnu/4.8.5/include/jni.h
/usr/java/jdk1.8.0_121/include/jni.h
$ gcc -fPIC -shared -I/usr/java/jdk1.8.0_121/include -I/usr/java/jdk1.8.0_121/include/linux -o javaJNI.so JavaJNI.c
$ ll
total 24
-rw-r--r-- 1 root root 353 Oct 12 10:48 JavaJNI.c
-rw-r--r-- 1 root root 521 Oct 12 10:48 JavaJNI.class
-rw-r--r-- 1 root root 557 Oct 12 10:48 JavaJNI.h
-rw-r--r-- 1 root root 324 Oct 12 10:46 JavaJNI.java
-rwxr-xr-x 1 root root 7954 Oct 12 10:51 javaJNI.so
5)运行:
$ pwd
/data/kevinliu/jni
$ mv javaJNI.so /data
$ java JavaJNI
hello jni
wo shi ben di diao yong ce shi
到这里,我们就完成了通过jni让java调用C代码。接下来,我们试试java调用C++代码。
1)java代码:JavaJNI.java
同上
2)编译、导出头文件:
同上
3)编写C++代码,实现对应的方法:JavaJNI.cpp
#include "JavaJNI.h"
#include
using namespace std;
JNIEXPORT void JNICALL Java_JavaJNI_printstr__(JNIEnv *env, jobject obj) {
cout<<"hello jni cpp..."<GetStringUTFChars(string,0);
cout<
这里需要注意JNIEnv 的使用:
对于JNIEnv *env来说,在C中调用:(*env)->NewStringUTF(env, "Hello from JNI!");
而在C++中如果按照上述调用则会发生'base operand of '->' has non-pointer type '_JNIEnv''错误,需要如下调用:
env->NewStringUTF("Hello from JNI!");
原因:参见jni.h中对于JNIEnv的定义:
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
#else
typedef const struct JNINativeInterface* JNIEnv;
#endif
4)编译c++,生成动态链接库:
$ g++ -c JavaJNI.cpp
JavaJNI.cpp: In function ‘void Java_JavaJNI_printstr__Ljava_lang_String_2(JNIEnv*, jobject, jstring)’:
JavaJNI.cpp:12:27: error: base operand of ‘->’ has non-pointer type ‘JNIEnv {aka _Jv_JNIEnv}’
const char *str = (*env)->GetStringUTFChars(env,string,0);
#需要按照上面的方式改动env
$ g++ -fPIC -shared -I/usr/java/jdk1.8.0_121/include -I/usr/java/jdk1.8.0_121/include/linux -o javaJNI.so JavaJNI.cpp
$ ll
-rw-r--r-- 1 root root 521 Oct 12 10:48 JavaJNI.class
-rw-r--r-- 1 root root 370 Oct 12 10:56 JavaJNI.cpp
-rw-r--r-- 1 root root 557 Oct 12 10:48 JavaJNI.h
-rw-r--r-- 1 root root 324 Oct 12 10:46 JavaJNI.java
-rwxr-xr-x 1 root root 8638 Oct 12 10:57 javaJNI.so
5)运行:
$ mv javaJNI.so /data
$ java JavaJNI
hello jni cpp...
wo shi ben di diao yong ce shi
最后