JNI 开发流程
- 编写声明了 native 方法的 Java 类
- 将 Java 源代码编译成 class 字节码文件
- 用 javah -jni 命令生成
.h
头文件(javah 是 jdk 自带的一个命令,-jni 参数表示将 class 中用native 声明的函数生成 JNI 规则的函数) - 用本地代码实现
.h
头文件中的函数 - 将本地代码编译成动态库(Windows:_.dll,linux/unix:_.so,mac os x:\*.jnilib)
- 拷贝动态库至 java.library.path 本地库搜索目录下,并运行 Java 程序
HelloWorld例程
1.新建一个 HelloWorld.java 源文件
package com.win.jni;
public class HelloWorld {
public static void main(String[] args){
System.out.println(sayHello("MorrisWare"));
}
public static native String sayHello(String name);
static {
System.loadLibrary("HelloWorld");
//System.load("/home/morrisware/JNI/HelloWorld/lib/libHelloWorld.so"
}
}
2.用 javac 命令将.java
源文件编译成.class
字节码文件
➜ HelloWorld mkdir bin
➜ HelloWorld javac src/com/win/jni/HelloWorld.java -d ./bin/
-d 表示将编译后的 class 文件放到指定的目录下
3.用 javah -jni 命令,根据class
字节码文件生成.h
头文件(-jni 参数是可选的)
➜ HelloWorld mkdir jni
➜ HelloWorld javah -jni -classpath ./bin/ -d jni com.win.jni.HelloWorld
-classpath 类搜索路径,这里表示从当前的 bin 目录下查找
-d 将生成的头文件放到当前的 jni 目录下
4.用本地代码实现.h头文件中的函数
- com_win_jni_HelloWorld.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_win_jni_HelloWorld */
#ifndef _Included_com_win_jni_HelloWorld
#define _Included_com_win_jni_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_win_jni_HelloWorld
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_win_jni_HelloWorld_sayHello
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
- com_win_jni_HelloWorld.c
#include "com_win_jni_HelloWorld.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* * Class: com_win_jni_HelloWorld
* * Method: sayHello
* * Signature: (Ljava/lang/String;)Ljava/lang/String;
* */
JNIEXPORT jstring JNICALL Java_com_win_jni_HelloWorld_sayHello
(JNIEnv *env, jclass type, jstring name_){
const char *name = (*env)->GetStringUTFChars(env,name_,0);
char buff[100];
if(name == NULL){
printf("out of memory.\n");
return;
}
printf("Java Str:%s\n",name);
sprintf(buff,"hello %s",name);
(*env)->ReleaseStringUTFChars(env,name_,name);
return (*env)->NewStringUTF(env,buff);
}
#ifdef __cplusplus
}
#endif
5.将 C/C++ 代码编译成本地动态库文件动态库文件名命名规则:lib+动态库文件名+后缀(操作系统不一样,后缀名也不一样)如:
- Mac OS X : libHelloWorld.jnilib
- Windows :HelloWorld.dll(不需要 lib 前缀)
- Linux/Unix:libHelloWorld.so
Linux/Unix
➜ HelloWorld mkdir lib
➜ jni gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared
com_win_jni_HelloWorld.c -o ../lib/libhello.so
参数说明:
- -I: 包含编译JNI必要的头文件
- -fPIC: 编译成与位置无关的独立代码
- -shared:编译成动态库
- -o: 指定编译后动态库生成的路径和文件名
6.运行 Java 程序
- 加载动态库
System.loadLibrary("hello");
System.load("/home/morrisware/JNI/HelloWorld/lib/libhello.so");
- 让 Java 从 java.library.path 找到动态链接库文件
java -Djava.library.path=/home/morrisware/JNI/HelloWorld/lib -classpath ./bin com.win.jni.HelloWorld