为了更好的获得一些比较独立的模块的性能,比如视频模块,寻路模块,通过对C++ 接口的封装,通过JNI技术对它进行跨语言调用。
那什么是JNI呢?JNI (Java Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。
- 生成头文件
- java ->*.h 工具
- 编辑C++ 文件
- 类型互转
- 生成 DLL,SO 文件
- CMAKE 文件
- make_win64.bat & make_linux64.sh 文件
- 加载DLL,SO 文件
- 调用JNI 接口
首先要编辑头文件对应的JAVA文件,就是暴露出native接口的JAVA类。
public class GamiooJNI {
static {
try {
NativeUtils.loadLibrary("recast");
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
}
}
public native String getVersion();
}
java ->*.h 工具
点击 File > Settings > Tools > External Tools,添加一个先的External Tools:
Name:Generate Header File Description: 生成C++类的头文件 Program: $JDKPath$/bin/javah Arguments: -jni -classpath $OutputPath$ -d ./jni $FileClass$ Working directory: $ProjectFileDir$
做好后,在某个java文件导出
在GamiooJNI.java文件中点击右键> External Tools > Generate Header File
如果生成不出来的话,编译下该Java文件,并把*.h文件给删了先。
实际上,就是执行了类似如下指令:
"D:Program FilesJavaTencentKona-8.0.4/bin/javah" -jni -classpath F:gamioooutproductionclasses -d ./jni com.gamioo.ooxx.GamiooJNI
生成的文件:com_gamioo_ooxx_GamiooJNI.h
#include编辑C++ 文件#ifndef _Included_com_gamioo_ooxx_GamiooJNI #define _Included_com_gamioo_ooxx_GamiooJNI #ifdef __cplusplus extern "C" { #endif JNIEXPORT jstring JNICALL Java_com_gamioo_ooxx_GamiooJNI_getVersion (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
用CLion去创建dll工程,引入头文件com_gamioo_ooxx_GamiooJNI.h
创建cpp文件
#include "jni.h" #include#include #include #include #include
这里需要注意,一开始第二行的#include
有很多类型的互转需要注意,类型互转问题转不好,还有内存泄漏的问题,这里会陆续总结:
static char* ConvertJByteaArrayToChars(JNIEnv* env, jbyteArray bytearray)
{
char* chars = NULL;
jbyte* bytes;
bytes = env->GetByteArrayElements(bytearray, 0);
size_t chars_len = env->GetArrayLength(bytearray);
chars = new char[chars_len + 1];
memset(chars, 0, chars_len + 1);
memcpy(chars, bytes, chars_len);
chars[chars_len] = 0;
env->ReleaseByteArrayElements(bytearray, bytes, 0);
return chars;
}
static jfloatArray ConvertFloatStarToJfloatArray(JNIEnv* env, float* array, int length) {
jfloatArray ret = env->NewFloatArray(length);
env->SetFloatArrayRegion(ret, 0, length, array);
return ret;
}
jstring stringTojstring(JNIEnv* env, const char* pat) {
jclass strClass = (env)->FindClass("java/lang/String");
jmethodID ctorID = (env)->GetMethodID(strClass, "", "([BLjava/lang/String;)V");
jbyteArray bytes = (env)->NewByteArray(strlen(pat));
(env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = (env)->NewStringUTF("GB2312");
return (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
}
std::string jstringTostring(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("GB2312");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0) {
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
std::string stemp(rtn);
free(rtn);
return stemp;
}
std::string toStr(JNIEnv* env, jstring jstr)
{
return toStr(env, env->GetStringUTFChars(jstr, 0));
}
std::string toStr(JNIEnv* env, const char* chs)
{
std::string s(chs);
return s;
}
jstring toJstring(JNIEnv* env, std::string str)
{
return toJstring(env, str.c_str());
}
jstring toJstring(JNIEnv* env, char* chs)
{
return env->NewStringUTF(chs);
}
生成 DLL,SO 文件
CMAKE 文件
CMakeLists.txt 的内容如下,依照葫芦画瓢就行。
cmake_minimum_required(VERSION 3.22)
find_package(JNI REQUIRED)
# Use C++11
set(CMAKE_CXX_STANDARD 11)
# Require (at least) it
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Don't use e.g. GNU extension (like -std=gnu++11) for portability
set(CMAKE_CXX_EXTENSIONS OFF)
include_directories(${JNI_INCLUDE_DIRS})
if ( WIN32 AND NOT CYGWIN AND NOT ( CMAKE_SYSTEM_NAME STREQUAL "WindowsStore" ) )
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT" CACHE STRING "")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd" CACHE STRING "")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT" CACHE STRING "")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd" CACHE STRING "")
endif ()
project(RecastDll)
find_path(RecastDll_PROJECT_DIR NAMES SConstruct
PATHS
${CMAKE_SOURCE_DIR}
NO_DEFAULT_PATH
)
MARK_AS_ADVANCED(RecastDll_PROJECT_DIR)
# 配置cpp文件
file(GLOB RECASTDLL_SOURCES
Source
public class NativeUtils {
public static void loadLibrary(String name) throws IOException {
String suffix = "";
//TODO 暂时只为两种系统服务
if (SystemUtils.IS_OS_LINUX) {
suffix += ".so";
} else {
suffix += ".dll";
}
try (InputStream inputStream = FileUtils.getInputStream(name + suffix); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int n = 0;
while (-1 != (n = inputStream.read(buffer))) {
out.write(buffer, 0, n);
}
File file = File.createTempFile(name, suffix);
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
fileOutputStream.write(out.toByteArray());
}
System.load(file.getAbsolutePath());
}
}
}
然后在JNI接口类里加载进来:
static {
try {
NativeUtils.loadLibrary("recast");
} catch (IOException e) {
LOGGER.error(e.getMessage(), e);
}
}
调用JNI 接口
调用接口就像调用JAVA 普通的API一样,
GamiooJNI jni=new GamiooJNI (); String version=jni.getVersion();
JNI中接下去需要探索的: 自定义对象的转换
linux安装llvm
CentOS 7 升级安装 gcc7
cmake高版本安装及踩坑
CMake 指定gcc编译版本
python3和pip3安装和问题解决
JNI C++调用Java(一)
fastFFI 官宣开源,一款高效的Java跨语言通信框架
GraphScope analytics in Java:打破大规模图计算的跨语言障碍
JNI的替代者—使用JNA访问Java外部功能接口
用CLion实现本地方法并给java调用



