最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java調(diào)用C++動態(tài)庫(DLL)的完整實踐指南

 更新時間:2025年08月27日 09:04:04   作者:頗有幾分姿色  
這篇文章主要為大家詳細介紹如何通過 JNI(Java Native Interface)在 Java 中調(diào)用一個用 C++ 編寫的分割算法庫,有需要的小伙伴可以了解一下

Java 通過 JNI 調(diào)用 C++ 動態(tài)庫的完整流程

這里主要介紹如何通過 JNI(Java Native Interface)在 Java 中調(diào)用一個用 C++ 編寫的分割算法庫。不涉及圖像和 java 實體處理。環(huán)境如下:

  • 系統(tǒng):Windows 10
  • JDK:1.8
  • IDE:Visual Studio 2022
  • Java 構(gòu)建工具:Maven
  • 目標平臺:x64

一、整體目標

Java 端通過 JNI 調(diào)用算法提供的動態(tài)庫,實現(xiàn)圖像分割與信息提取的功能。為了方便,我把所有的依賴庫都放在了 jdk 的 bin 目錄,這樣我在調(diào)用的時候只需要導入我的自己生成的 jni 庫就可以,如果需要頻繁切換 jdk,那需要自己指定目錄,處理好動態(tài)庫的依賴關(guān)系即可。

二、準備工作

1. C++ 頭文件(API 定義)

這是算法的頭文件,jni 層需要根據(jù)他的方法調(diào)用。建議和 java 的方法保持一致,不保持一致也行,自己在 jni 層處理好就可以。

// passportSegC.h
DLL_API const char* passport_seg_get_version();
DLL_API int passport_seg_init(const int equip_type, const int passport_type, const char* model_path, const char* config_path);
DLL_API const char* passport_seg_run(const char* json_str_c, const char* save_root_c, int* code_passport_offset_det);
DLL_API void passport_seg_release_per_call(const char* json_ctr);
DLL_API void passport_seg_release();

2. Java 接口類定義

package com.emp.empxmrz.util;

/***
 * @title
 * @author 
 * @date 2025/8/7 10:58
 **/
public class PassportSeg {
    static {
        System.loadLibrary("passportSeg"); 
    }

    // 獲取版本號
    public static native String passportSegGetVersion();

    // 算法初始化
    public static native int passportSegInit(int equipType, int passportType, String modelPath, String configPath);

    // 調(diào)用算法
    public static native String passportSegRun(String jsonStr, String saveRoot, int[] codePassportOffsetDet);

    // 每次調(diào)用算法后釋放內(nèi)存
    public static native void passportSegReleasePerCall(String jsonCtr);

    // 程序終止時釋放AI模型內(nèi)存
    public static native void passportSegRelease();
}

三、生成 JNI 頭文件

javah -classpath target/classes -d src/main/jni com.emp.empxmrz.util.PassportSeg

這會生成 com_emp_empxmrz_util_PassportSeg.h,它定義了 JNI 接口供 C++ 實現(xiàn)。生成之后不能隨便移動類的位置或修改包名、類名等,如果必須調(diào)整的話,需要重新生成。

生成的頭文件大概如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_emp_empxmrz_util_PassportSeg */

#ifndef _Included_com_emp_empxmrz_util_PassportSeg
#define _Included_com_emp_empxmrz_util_PassportSeg
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegGetVersion
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegGetVersion
  (JNIEnv *, jclass);

/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegInit
 * Signature: (IILjava/lang/String;Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegInit
  (JNIEnv *, jclass, jint, jint, jstring, jstring);

/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegRun
 * Signature: (Ljava/lang/String;Ljava/lang/String;[I)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegRun
  (JNIEnv *, jclass, jstring, jstring, jintArray);

/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegReleasePerCall
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegReleasePerCall
  (JNIEnv *, jclass, jstring);

/*
 * Class:     com_emp_empxmrz_util_PassportSeg
 * Method:    passportSegRelease
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegRelease
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

四、JNI 實現(xiàn)(C++)

這是 jni 的頭文件實現(xiàn),因為我是要調(diào)用多個算法,所以使用 vs 創(chuàng)建了一個解決方案,里面創(chuàng)建了多個項目,每個項目都是一種算法的 jni 層,根據(jù)自己的實際情況操作就可以,實現(xiàn)類中需要包含算法頭文件和 jni 頭文件。項目結(jié)果大致如下:

cpp 完整代碼如下:

#include <jni.h>
#include <iostream>
#include <stdexcept>
#include "com_emp_empxmrz_util_PassportSeg.h"
#include "passportSegC.h"

void native_log(const std::string& message) {
    std::cerr << "[NativeLog] " << message << std::endl;
}

void throwJavaException(JNIEnv* env, const char* message) {
    jclass exceptionCls = env->FindClass("java/lang/RuntimeException");
    if (exceptionCls != nullptr) {
        env->ThrowNew(exceptionCls, message);
    }
}

extern "C" {

    JNIEXPORT jstring JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegGetVersion
    (JNIEnv* env, jclass clazz) {
        try {
            std::cout << "passport seg get version........" << std::endl;
            const char* version = passport_seg_get_version();
            return env->NewStringUTF(version);
        }
        catch (const std::exception& e) {
            native_log(e.what());
            throwJavaException(env, e.what());
            return env->NewStringUTF("error");
        }
        catch (...) {
            native_log("Unknown error in passportSegGetVersion");
            throwJavaException(env, "Unknown error in passportSegGetVersion");
            return env->NewStringUTF("error");
        }
    }

    JNIEXPORT jint JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegInit
    (JNIEnv* env, jclass clazz, jint equipType, jint passportType, jstring modelPath, jstring configPath) {
        try {
            const char* model_path = env->GetStringUTFChars(modelPath, 0);
            const char* config_path = env->GetStringUTFChars(configPath, 0);

            int result = passport_seg_init(equipType, passportType, model_path, config_path);

            env->ReleaseStringUTFChars(modelPath, model_path);
            env->ReleaseStringUTFChars(configPath, config_path);
            return result;
        }
        catch (const std::exception& e) {
            native_log(e.what());
            throwJavaException(env, e.what());
            return -1;
        }
        catch (...) {
            native_log("Unknown error in passportSegInit");
            throwJavaException(env, "Unknown error in passportSegInit");
            return -1;
        }
    }

    JNIEXPORT jstring JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegRun
    (JNIEnv* env, jclass clazz, jstring jsonStr, jstring saveRoot, jintArray codeArray) {
        try {
            if (!jsonStr || !saveRoot || !codeArray) {
                native_log("Null input detected.");
                throwJavaException(env, "Null input parameter.");
                return nullptr;
            }
            const char* json_str = env->GetStringUTFChars(jsonStr, 0);
            const char* save_root = env->GetStringUTFChars(saveRoot, 0);
            jint* codes = env->GetIntArrayElements(codeArray, NULL);

            if (!json_str || !save_root || !codes) {
                native_log("Failed to convert jstring/jintArray.");
                throwJavaException(env, "JNI conversion failed.");
                return nullptr;
            }
            native_log("Calling passport_seg_run...");
            const char* result = passport_seg_run(json_str, save_root, reinterpret_cast<int*>(codes));

            jstring jResult = nullptr;
            if (result != nullptr) {
                jResult = env->NewStringUTF(result); // 拷貝內(nèi)容
                passport_seg_release_per_call(result); // 安全釋放
            }
            else {
                jResult = env->NewStringUTF("");
            }

            env->ReleaseStringUTFChars(jsonStr, json_str);
            env->ReleaseStringUTFChars(saveRoot, save_root);
            env->ReleaseIntArrayElements(codeArray, codes, 0);

            return jResult;
        }
        catch (const std::exception& e) {
            native_log(std::string("[C++ Exception] ") + e.what());
            throwJavaException(env, e.what());
            return nullptr;
        }
        catch (...) {
            native_log("Unknown error in passportSegRun");
            throwJavaException(env, "Unknown error in passportSegRun");
            return nullptr;
        }
    }

    JNIEXPORT void JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegReleasePerCall
    (JNIEnv* env, jclass clazz, jstring jsonCtr) {
        try {
            const char* json_ctr = env->GetStringUTFChars(jsonCtr, 0);
            passport_seg_release_per_call(json_ctr);
            env->ReleaseStringUTFChars(jsonCtr, json_ctr);
        }
        catch (const std::exception& e) {
            native_log(e.what());
            throwJavaException(env, e.what());
        }
        catch (...) {
            native_log("Unknown error in passportSegReleasePerCall");
            throwJavaException(env, "Unknown error in passportSegReleasePerCall");
        }
    }

    JNIEXPORT void JNICALL Java_com_emp_empxmrz_util_PassportSeg_passportSegRelease
    (JNIEnv* env, jclass clazz) {
        try {
            passport_seg_release();
        }
        catch (const std::exception& e) {
            native_log(e.what());
            throwJavaException(env, e.what());
        }
        catch (...) {
            native_log("Unknown error in passportSegRelease");
            throwJavaException(env, "Unknown error in passportSegRelease");
        }
    }

}

建議添加異常處理,避免JVM 崩潰。

五、Visual Studio 配置

1. 添加包含目錄

打開【項目屬性】 > C/C++ > 常規(guī) > 附加包含目錄

防止編譯階段報錯。

2. 添加庫目錄

打開【鏈接器】 > 常規(guī) > 附加庫目錄

3. 添加依賴庫

打開【鏈接器】 > 輸入 > 附加依賴項

告訴編譯器如何調(diào)用 .dll 中的函數(shù);

4.生成 DLL

編譯后會生成 passportSeg.dllpassportSeg.lib,將他們復制到 jdk 的 bin 目錄或者你自己定義的目錄下, java 就可以直接調(diào)用 passportSeg了。

六、Java 調(diào)用測試

java 測試結(jié)果是否正確,也可以自己封裝成接口。

public static void main(String[] args) {
    System.out.println("版本號>>>>" + PassportSeg.passportSegGetVersion());

    int init = PassportSeg.passportSegInit(1, 1, MODEL_PATH, CONFIG_PATH);
    if (init != 0) {
        System.out.println("Init failed: " + init);
        return;
    }

    int[] code = new int[1];
    String resultJson = PassportSeg.passportSegRun(IMG_PATH, SAVE_PATH, code);

    System.out.println("Result: " + resultJson);
    System.out.println("Error Code: " + code[0]);
     // JNI 層已自動調(diào)用,這里不需要重復調(diào)用
    // PassportSeg.passportSegReleasePerCall(resultJson);
    PassportSeg.passportSegRelease();
}

調(diào)試建議

如果報錯找不到 passportSeg.dll,請將該 DLL 放入:

  • 項目運行目錄;
  • 或者 jdk/bin 目錄;
  • 或者設置 -Djava.library.path。

如果 JNI 函數(shù)名對應不上,請確保:

  • 包名、類名、方法名匹配;
  • DLL 導出的函數(shù)使用 extern "C"。
  • JNI 方法未正確導出,可通過.def文件顯式指定導出符號。

七、總結(jié)

  • 編寫 Java native 接口;
  • 使用 javah 生成 JNI 頭文件;
  • C++ 實現(xiàn) JNI 方法;
  • 配置 Visual Studio 編譯動態(tài)庫;
  • Java 調(diào)用測試。

如果你不想污染自己的JDK bin,可以將 .dll.lib 放到一個統(tǒng)一的目錄下,這樣的話切換 jdk 比較方便,因為我只使用 jdk8,所以貪圖方便放在了 bin 目錄,但這是不規(guī)范滴 。

到此這篇關(guān)于Java調(diào)用C++動態(tài)庫(DLL)的完整實踐指南的文章就介紹到這了,更多相關(guān)Java調(diào)用C++動態(tài)庫DLL內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Boot身份認證狀態(tài)的存儲與傳遞實現(xiàn)方案

    Spring Boot身份認證狀態(tài)的存儲與傳遞實現(xiàn)方案

    本文主要內(nèi)容介紹了在SpringBoot中實現(xiàn)用戶登錄信息獲取的三種主流方案:基于Session+Cookie、基于JWTToken和基于OAuth2.0/OIDC,并詳細介紹了每種方案的核心邏輯、代碼實現(xiàn)、優(yōu)缺點及關(guān)鍵注意點,感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • Java中的CountDownLatch同步工具類使用解析

    Java中的CountDownLatch同步工具類使用解析

    這篇文章主要介紹了Java中的CountDownLatch使用解析,CountDownLatch初始化的時候必須指定一個count,await方法會一直阻塞直到調(diào)用countdown方法,count為0,當count為0時,所有的等待線程都會被釋放,需要的朋友可以參考下
    2023-12-12
  • Java Apache POI實現(xiàn)導出Excel的實戰(zhàn)指南

    Java Apache POI實現(xiàn)導出Excel的實戰(zhàn)指南

    在企業(yè)級開發(fā)中,Excel 數(shù)據(jù)導出是高頻需求,本文基于 Apache POI 實現(xiàn)災情速報員數(shù)據(jù) Excel 導出功能,文中的示例代碼講解詳細,感興趣的小伙伴可以了解下
    2026-04-04
  • 詳解IDEA中類加載器調(diào)用getResourceAsStream()方法需注意的問題

    詳解IDEA中類加載器調(diào)用getResourceAsStream()方法需注意的問題

    這篇文章主要介紹了詳解IDEA中類加載器調(diào)用getResourceAsStream()方法需注意的問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-02-02
  • ?java簡介及環(huán)境搭建

    ?java簡介及環(huán)境搭建

    這篇文章主要介紹了java簡介及環(huán)境搭建,文章主要介紹Java的發(fā)展史及環(huán)境搭建,對正在學Java的你有一定的參考價值,需要的小伙伴可以參考一下
    2022-03-03
  • SpringSecurity實現(xiàn)多種身份驗證方式

    SpringSecurity實現(xiàn)多種身份驗證方式

    本文主要介紹了SpringSecurity實現(xiàn)多種身份驗證方式,包括表單的認證、HTTP基本認證、HTTP摘要認證、證書認證、OpenIDConnect或OAuth2.0的認證、記住我功能和LDAP認證,感興趣的可以了解一下
    2025-03-03
  • 網(wǎng)易Java程序員兩輪面試 請問你能答對幾個?

    網(wǎng)易Java程序員兩輪面試 請問你能答對幾個?

    為大家分享網(wǎng)易Java程序員兩輪面試題,考考大家,這些問題你能答對幾個?
    2017-11-11
  • Java SSL與TLS客戶端證書配置方式

    Java SSL與TLS客戶端證書配置方式

    這篇文章主要介紹了Java SSL與TLS客戶端證書配置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • System.identityHashCode和hashCode的區(qū)別及說明

    System.identityHashCode和hashCode的區(qū)別及說明

    String調(diào)用hashCode()和System.identityHashCode()返回值不同是因為String重寫了hashCode()方法,而System.identityHashCode()返回對象的內(nèi)存地址哈希值;Test調(diào)用兩個方法返回值相同是因為Test沒有重寫hashCode()方法,因此兩者調(diào)用底層的JVM_IHashCode方法返回相同值
    2024-11-11
  • Spring cloud alibaba之Ribbon負載均衡實現(xiàn)方案

    Spring cloud alibaba之Ribbon負載均衡實現(xiàn)方案

    Spring cloud Ribbon是基于Netflix Ribbon實現(xiàn)的一套客戶端的負載均衡工具,Ribbon客戶端提供一系列完善的配置,如超時、重試等,Ribbon也可以實現(xiàn)自己的負載均衡算法,感興趣的朋友跟隨小編一起看看吧
    2021-07-07

最新評論

曲阜市| 内丘县| 乌鲁木齐县| 临颍县| 安化县| 响水县| 浑源县| 新营市| 阳山县| 隆昌县| 台中县| 永新县| 峨眉山市| 五常市| 宿州市| 汶川县| 廊坊市| 松江区| 石城县| 广丰县| 辽源市| 五大连池市| 丰城市| 张家川| 定边县| 石渠县| 合川市| 门源| 新源县| 云阳县| 集安市| 金阳县| 稷山县| 日喀则市| 珠海市| 榆林市| 竹北市| 湘潭市| 达日县| 富锦市| 屯昌县|