说明:本文借助一个简单的Holle World例程,在Linux平台上演示如何使用JNI,实现Java调用C的库。
JIN说明
JNI是Java native interface的简写,可以译作Java原生接口。
Java可以通过JNI调用C/C++的库,这对于那些对性能要求比较高的Java程序无疑是一个福音。
JNI是Java与C/C++交互的接口。
使用JNI也是有代价。大家都知道JAVA程序是运行在JVM之上的,可以做到平台无关。
但是如果Java程序通过JNI调用了原生的代码(比如c/c++等),则Java程序就丧失了平台无关性。
最起码需要重新编译原生代码部分。所以应用JNI需要好好权衡,不到万不得已,请不要选择JNI,
1.编写TestJni.java文件
package mdjpos.testjni;//指定根目录位置
import java.util.*;
public class TestJni
{
//native关键字表明方法是用其他语言实现的
public native void print(String content);
static
{
System.loadLibrary("TestJni");
// load native library exclude extension name.
}
}
注意print方法的声明,关键字native表明该方法是一个原生代码实现的。
另外注意static代码段的System.loadLibrary调用,这段代码表示在程序加载的时候,自动加载libTestJni.so库。
TestJni.c文件保存路径: ***jni_test\mdjpos\testjni
说明:***为省略的工程文件夹所在的路径,jni_test为工程的根目录文件。
2.生成 TestJni.h文件
(1)执行命令:javah -jni -d ./so_src mdjpos.testjni.TestJni
javah:shell指令,用于生成".h"文件
-jni:生成JIN样式的标头文件(默认值)
-d ./so_src:指定输出文件的路径为:./so_src
mdjpos.testjni.TestJni:头文件名和函数名
扩展:可以输入“javah -help”,查询javah的用法。
在jni_test\so_src下,可查看到生成的头文件mdjpos_testjni_TestJni.h。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class mdjpos_testjni_TestJni */
#ifndef _Included_mdjpos_testjni_TestJni
#define _Included_mdjpos_testjni_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: mdjpos_testjni_TestJni
* Method: print
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_mdjpos_testjni_TestJni_print
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
(2)修改头文件名
将文件名“mdjpos_testjni_TestJni.h”改成“TestJni.h”。
3.创建TestJni.c文件
#include <jni.h>
#include <stdio.h>
#include <TestJni.h>
JNIEXPORT void JNICALL
Java_mdjpos_testjni_TestJni_print(JNIEnv *env,jobject obj, jstring content)
{
// 从 instring 字符串取得指向字符串 UTF 编码的指针
//注意C语言必须(*env)-> C++ env->
const jbyte *str =(const jbyte *)(*env)->GetStringUTFChars(env,content, JNI_FALSE);
printf("Hello---->%s\n",str);
// 通知虚拟机本地代码不再需要通过 str 访问 Java 字符串。
(*env)->ReleaseStringUTFChars(env, content, (const char *)str );
return;
}
//这里看到 JNIEnv作用了,使得我们可以使用Java的方法
//jobject 指向在此 Java 代码中实例化的 Java 对象 LocalFunction 的一个句柄,相当于 this 指针
参数类型 jstring 对应java中的String,这里是有所不同的。每一个Java里的类型这里有对应的与之匹配。
4.生成:libTestJni.so库文件
gcc -I /usr/lib/jvm/java-8-openjdk-amd64/include/linux/ -I /usr/lib/jvm/java-8-openjdk-amd64/include/ -I ./so_src/ -fPIC -shared -o ./libs/libTestJni.so ./so_src/TestJni.c
-I /usr/lib/jvm/java-8-openjdk-amd64/include/linux/:指定头文件路径。(这里具体对应哪个头文件暂时还不清楚,望大神指教)
-I /usr/lib/jvm/java-8-openjdk-amd64/include/:指定头文件“jni.h”的路径。
-I ./so_src/:指定头文件“TestJni.h”的路径。
-fPIC:作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。
-shared:生成动态库
-o ./libs/libTestJni.so:指定生成库文件的路径和名称。
./so_src/TestJni.c:指定源文件的路径和名称。
5.创建Test_main.java文件
import java.util.*;
import mdjpos.testjni.*;
public class Test_main
{
public Test_main()
{
}
public static void main(String argv[])
{
TestJni m_TestJni;
m_TestJni = new TestJni();
m_TestJni.print("Hello,World !");
}
}
编译, javac Test_main.java
6.运行
java -Djava.library.path=./libs Test_main
java :运行指令。
-Djava.library.path=./libs :指定动态库文件路径。
Test_main:运行的class文件名。
7.常见错误
问题:未找到动态库文件。
原因:动态库文件的路径错误,未查找到库。将“./”改成“./libs”,动态库文件不在根目录,在根目录下的libs文件中。
参考文件:https://blog.csdn.net/yanhe156/article/details/80521553