Part1
Java Native Interface-JNI-JAVA本地调用
JNI标准是Java平台的一部分, 允许Java代码和其他语言进行交互;
开始实现->
Step 1) 编写Java代码, 编写一个JNI接口HelloJNI.java
1
2
3
4
5
6
7
8
9
10
11
|
public
class
HelloJNI {
static
{
System.loadLibrary(
"hello"
);
// hello.dll (Windows) or libhello.so (Unixes)
}
// A native method that receives nothing and returns void
private
native
void
sayHello();
public
static
void
main(String[] args) {
new
HelloJNI().sayHello();
// invoke the native method
}
}
|
sayHello()是一个native方法, 这个方法会在生成的JNI header文件中声明C/C++的函数;
loadLibrary()会在当前路径(实际上是Java Library Path)下寻找并加载名为hello的动态库, 所有的dependency都会在当前路径下加载;
对于不同的平台loadLibrary()会自动搜索不同的后缀名; e.g. Sample.dll(Windows), libSample.so(Linux);
你也可以指定路径: "-Djava.library.path=path_to_lib", 路径错误的话会有"UnsatisfiedLinkError";
相应还有load()函数, 需要指定路径和dependency的路径;
dlltool http://sourceware.org/binutils/docs/binutils/dlltool.html
Note 动态库加载必须在static块内, 保证首先进行;
Step 2) 编译和生成C/C++ header
1
|
javac HelloJNI.java
|
编译Java生成class;
1
|
javah HelloJNI
|
javah命令会生成相应的header: HelloJNI.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */
#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern
"C"
{
#endif
/*
* Class: HelloJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT
void
JNICALL Java_HelloJNI_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
|
在相应的cpp文件中实现函数: JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject);
名字转换的格式: Java_{package_and_classname}_{function_name}(JNI arguments)
-JNIEnv*参数: 指向JNI的环境, 给你调用JNI函数的权限;
-jobject参数: 指向Java的"this"对象;
-extern "C"会被C++编译器识别, 把函数用C的命名方式来编译.
Step 3) 编译C/C++库
HelloJN.cpp的实现:
1
2
3
4
5
6
|
#include <stdio.h>
#include "HelloJNI.h"
JNIEXPORT
void
JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
printf
(
"Hello World!\n"
);
return
;
}
|
>jni.h的路径一般是在<JAVA_HOME>\include" 和 "<JAVA_HOME>\include\win32";
Note 不同环境下的编译选项是不同的;
Windows下的gcc必须加上一个参数 "-Wl,--add-stdcall-alias -shared";
VC++的cl和Linux下的gcc不需要这个参数;
1
|
gcc -Wl,--add-stdcall-
alias
-I
"<JAVA_HOME>\include"
-I
"<JAVA_HOME>\include\win32"
-shared -o hello.dll HelloJNI.cpp
|
>"-Wl"会把选项"--add-stdcall-alias"传输给链接器, 防止"UnsatisifiedLinkError".(一般导出的名字有"@nn"的前缀, 这个选项会把导出的名字加上没有前缀的别名)
有些时候也会使用 "-Wl,--kill-at".
>"-I"指定JNI头文件路径, 路径有空格时加上双引号.
可以使用nm命令列出函数导出的外部符号:
1
|
nm hello.dll |
grep
say
|
>windows: "nm -g file.dll"
Step 4) 运行JNI程序
1
|
java -Djava.library.path=. HelloJNI
|
>"-Djava.library.path=<path_to_lib>" 是可选的, 作为虚拟机的选项来制定动态库的路径.
Linux下可能需要设置路径:
1
|
export
LD_LIBRARY_PATH=.
|
设置library路径为当前目录, 或者将so放入java执行目录下;
Other
编译链接相关
alias name that without @
"gcc -Wl,--add-stdcall-alias -I"C:\Program Files (x86)\Java\jdk1.7.0_17\include" -I"C:\Program Files (x86)\Java\jdk1.7.0_17\include\win32" -shared -o HelloWorld.dll HelloWorld.c"
"cl -I%java_home%\include -I%java_home%\include\win32 -LD HelloWorldImp.c -Fehello.dll"
Compile time and Link Time: -L, -I, -Wl,rpath=<your_lib_dir>
Linux: LD_LIBRARY_PATH; ldd; ldconfig; nm; readlf; Id;
<refer to> http://blog.csdn.net/unbutun/article/details/6362474 & http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
JNI header
classpath: should point to the root folder where your top level package (JNI) goes to, not to the folder where your class is physically located.
http://stackoverflow.com/questions/14795050/javah-command-for-native-methods-gives-exception
1) "javah HelloWorld" (all the config is set)
2) "javah -o "JNIDemo.h" -jni -classpath "R:\Projects\JAVA\JavaJNIDemo\build\classes" javajnidemo.JavaJNIDemo"
1
|
javah -o
"<HeaderPATH>\JNIHeader.h"
-jni -classpath
"JavaClassPath"
JNISample
|
JNI在Package里的case
1
|
package myjni;
//...Java codes
|
>JNI的类会被放入"myjni"的package内, 文件保存为"myjni\HelloJNI.java"
编译: 加上package(路径)名
1
|
javac myjni\HelloJNI.java
|
javah: 使用package名, 把头文件生成到include文件夹下.
1
|
javah -d include myini.HelloJNI
|
头文件的native函数: Java_<fully-qualified-name>_methodName
1
|
JNIEXPORT
void
JNICALL Java_myjni_HelloJNI_sayHello(JNIEnv *, jobject);
|
---End---