JNI是Java Native Interface的缩写,Java源生接口/本地调用/本地接口,允许Java和其他语言代码进行互相调用,Java可以调用C/C++代码,C/C++也可以调用Java的。Java程序运行在JVM上,所以平台无关,但通过JNI调用原生代码C/C++就会丧失这个特性,破坏可移植性,至少原生代码部分总是需要重新编译的。但是一下情况是可以接受的。
1.使用一些旧的库
2.与硬件、操作系统进行交互
3.为了提高程序的性能
Note:jni.h
就在%JAVA_HOME%\include下,还有%JAVA_HOME%\include\win32下的jni_md.h
。
Warning:javah
在Java10,生成C/C++头文件的功能由javac -h
代替
编写JAVA类
static
代码段的System.load
调用,这段代码表示在程序加载的时候,自动加载libHello.so
库。
区分一下加载动态库的2种方式
一个概念是JVM有两个重要变量java.class.path
和java.library.path
。前者指明了package的路径,后者指明了动态链接库的路径。
System.load();
绝对路径
System.loadLibrary ();
从java.library.path
下加载,不需要指明扩展名
public class Hello{
public native static String sayHello(String name);
static{
System.load("/usr/project/test/libHello.so");
#System.loadLibrary("Hello");
}
public static void main(String[] args){
Hello hello = new Hello();
String ret = hello.sayHello("Simona");
System.out.println(ret);
}
}
编译JAVA类
javac Hello.java
生成Hello.h
javah -jni Hello
or
javac -h . Hello
Note:JAVA 10弃用了javah
有包名的情况下需要指定classpath,不然会出现UnsatisfiledLinkError
javah -classpath 'your package path' -jni 'package'.Hello
or
指明共享库路径
java -Djava.library.path=. Hello
export LD_LIBRARY_PATH=“Hello路径”:$LD_LIBRARY_PATH
编写Native Method
生成的Hello.h的头文件带有需要实现的Native Method,实现这个Native Method需要指定参数名称
cat Hello.h
vim hello.c
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Hello */
#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Hello
* Method: sayHello
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_Hello_sayHello
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
#include <stdio.h>
#include "Hello.h"
JNIEXPORT jstring JNICALL Java_Hello_sayHello(JNIEnv *env, jclass jc, jstring name)
{
const char *buf;
buf = (*env)->GetStringUTFChars(env, name, NULL);
if (NULL == buf)
{
return NULL;
}
printf("%s\n", buf);
(*env)->ReleaseStringUTFChars(env, name, buf);
return (*env)->NewStringUTF(env, "hello");
}
GetStringUTFChars
返回指向字符串的UTF-8
字符数组的指针,如果不能创建这个字符数组,返回 null。该数组在被ReleaseStringUTFChars()
释放前将一直有效
const char* GetStringUTFChars(jstring string,jboolean* isCopy)
NewStringUTF
利用UTF-8
字符数组构造新java.lang.String对象
(*env)->NewStringUTF(env, "hello");
制作动态库
由于是Linux平台,需要制作后缀是.so的动态库,其中,需要指定jni.h的路径,必要时还需要jni_md.h的路径,该文件在jdk的目录中。这里指定了jni.h和jni_md.h的路径。
一个库的必须要是:lib+库名+.so。链接的时候只需要提供库名就可以了
find / -name jni.h
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/include/jni.h
find / -name jni_md.h
/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/include/linux/jni_md.h
gcc -c -fPIC -I/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/include/ -I/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.221-2.6.18.0.el7_6.x86_64/include/linux/ Hello.c -o Hello.o
gcc -shared Hello.o -o libHello.so
调用动态库
java Hello
Simona
hello
出现UnsatisfiedLinkError
pwd
/usr/project/test
export LD_LIBRARY_PATH=/usr/project/test:$LD_LIBRARY_PATH
java Hello
Simona
hello
or
java -Djava.library.path=. Hello
Simona
hello
创建调用类
import java.util.*;
public class HelloJni
{
public static void main(String argv[])
{
System.out.println( System.getProperty("java.library.path"));
new HelloJni();
}
public HelloJni()
{
new Hello().sayHello("Hello,Jni !"); //调用Hello的原生函数sayHello
}
}
执行调用
javac HelloJni.java
java HelloJni
/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
Hello,Jni !
网友评论