栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

JNI全流程实例使用总结

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JNI全流程实例使用总结

为了更好的获得一些比较独立的模块的性能,比如视频模块,寻路模块,通过对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 


#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

编辑C++ 文件

用CLion去创建dll工程,引入头文件com_gamioo_ooxx_GamiooJNI.h
创建cpp文件

#include "jni.h"
#include 
#include 
#include 
#include 
#include 
#include "com_gamioo_ooxx_GamiooJNI.h"
using namespace std;
static const int NAVMESHSET_VERSION = 1;

const char* NavMesh::Version()
{
	return  ""+ NAVMESHSET_VERSION;
}


JNIEXPORT jstring JNICALL Java_com_gamioo_ooxx_GamiooJNI_getVersion
(JNIEnv* env, jobject jobj) {
	const char* version = NavMesh::GetInstace()->Version();
	return env->NewStringUTF(version);
}

这里需要注意,一开始第二行的#include 报错了,这时因为MinGW编译器没有jni.h这个头文件,打开JDK的home目录,在include目录中可以找到jni.h头文件,除此之外,我们还需要include/win32目录下的jni_md.h头文件,一共两个,把这两个头文件都复制到MinGW安装目录(CLion 2021.3.3binmingwx86_64-w64-mingw32include目录中,注意这两个头文件是一起放在MinGW的这个目录的,jni_md.h不需要另外创建一个win32目录来存放。完成后发现com_example_jni_JNIObject.h的报错消失了。

类型互转

有很多类型的互转需要注意,类型互转问题转不好,还有内存泄漏的问题,这里会陆续总结:

    
	 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调用

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1011320.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号