Java 调用 C++ 动态库(DLL)完整实践:有图像有实体处理场景
Java 调用 C++ 动态库(DLL)完整实践:无图像无实体处理场景
这里主要介绍如何通过 JNI(Java Native Interface)在 Java 中调用一个用 C++ 编写的校色算法库。不涉及图像和 java 实体处理。环境如下:
- 系统:Windows 10
- JDK:1.8
- IDE:Visual Studio 2022
- Java 构建工具:Maven
- 目标平台:x64
一、整体目标
Java 端通过 JNI 调用算法提供的动态库,实现图像缺色偏色检测功能。为了方便,我把所有的依赖库都放在了 jdk 的 bin 目录,这样我在调用的时候只需要导入我的自己生成的 jni 库就可以,如果需要频繁切换 jdk,那建议自己配置 java.library.path
或将依赖库放到独立目录中 ,处理好动态库的依赖关系即可。
二、准备工作
1. C++ 头文件(API 定义)
这是算法的头文件,jni 层需要根据他的方法调用即可。建议和 java 的方法保持一致,不保持一致也可以,自己在 jni 层处理好就可以。
头文件方法定义:
// 版本号COLORCAST_API int ColorCastGetVersion(char* pOutBuf, int nOutBufSize);COLORCAST_API ColorCastResults analyzeImageColorCast(unsigned char* pData, int pw, int ph, int pchannels,double skin_ratio_threshold = 0.1, double C_threshold = 20.0,double Red_threshold = 18.0, double Green_threshold = 5.0,double Yellow_threshold = 30.0, double Blue_threshold = 5.0,double face_threshold = 0.5);COLORCAST_API ContrastColorCastResults contrastImageColorCast(unsigned char* tData, int tw, int th, int tchannels,unsigned char* pData, int pw, int ph, int pchannels,int YCrCb_cbmax = 125,int ROIwidthDiv = 6,int ROIheightDiv = 100,double USwidthRatio = 0.5,double USheightRatio = 0.01);
结构头文件:
// 偏色对比结果(双图比对)
struct ContrastColorCastResults {// 红色比对色偏指数double Red_Contrast = 999;// 绿色比对色偏指数double Green_Contrast = 999;// 黄色比对色偏指数double Yellow_Contrast = 999;// 蓝色比对色偏指数double Blue_Contrast = 999;// ROI偏色占比double ColorCast_ratio = 999;
};
2. Java 接口类定义
这里示例图像的三种传输方式:
- Java 层将图像转换为 BGR 格式的 byte[],直接传给 JNI;
- Java 层传原始图像数据,JNI 使用 OpenCV 解码;
- Java 层传递文件路径,JNI 自行读取文件内容。
package com.emp.empxmrz.util;import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;/**** @title* @author shijiangyong* @date 2025/8/27 11:23**/
public class ColorCastDetExampleJni {static {System.loadLibrary("ColorCastDetExampleJni");}// 获取版本号public static native int getVersion(byte[] buffer, int bufSize);// 对比图偏色检测// 传图片数据,处理成BGR格式public static native ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCast(byte[] tData, int tw, int th, int tchannels,byte[] photoData, int pw, int ph, int pchannels,int yCrCbCbmax, int roiWidthDiv, int roiHeightDiv,double usWidthRatio, double usHeightRatio);// 传原图片数据,不转格式public static native ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCastOri(byte[] tData,byte[] photoData,int yCrCbCbmax, int roiWidthDiv, int roiHeightDiv,double usWidthRatio, double usHeightRatio);// 传图片本地路径public static native ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCastByPath(String tData,String photoData,int yCrCbCbmax, int roiWidthDiv, int roiHeightDiv,double usWidthRatio, double usHeightRatio);@Datapublic static class ContrastColorCastResults {@Schema(description = "红色比对偏色指数")public double redContrast;@Schema(description = "绿色比对偏色指数")public double greenContrast;@Schema(description = "黄色比对偏色指数")public double yellowContrast;@Schema(description = "蓝色比对偏色指数")public double blueContrast;@Schema(description = "ROI区域偏色占比")public double colorCastRatio;}
}
三、生成 JNI 头文件
构建项目之后执行下面的命令。javah -classpath target/classes -d src/main/jni com.emp.empxmrz.util.ColorCastDetExampleJni
这会生成 com_emp_empxmrz_util_ColorCastDetExampleJni.h 和 com_emp_empxmrz_util_ColorCastDetExampleJni_ContrastColorCastResults.h
,它定义了 JNI 接口供 C++ 实现。生成之后不能随便移动类的位置或修改包名、类名等,如果必须调整的话,需要重新生成。
生成的头文件大概如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_emp_empxmrz_util_ColorCastDetExampleJni */#ifndef _Included_com_emp_empxmrz_util_ColorCastDetExampleJni
#define _Included_com_emp_empxmrz_util_ColorCastDetExampleJni
#ifdef __cplusplus
extern "C" {
#endif
/** Class: com_emp_empxmrz_util_ColorCastDetExampleJni* Method: getVersion* Signature: ([BI)I*/
JNIEXPORT jint JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_getVersion(JNIEnv *, jclass, jbyteArray, jint);/** Class: com_emp_empxmrz_util_ColorCastDetExampleJni* Method: contrastImageColorCast* Signature: ([BIII[BIIIIIIDD)Lcom/emp/empxmrz/util/ColorCastDetExampleJni/ContrastColorCastResults;*/
JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCast(JNIEnv *, jclass, jbyteArray, jint, jint, jint, jbyteArray, jint, jint, jint, jint, jint, jint, jdouble, jdouble);/** Class: com_emp_empxmrz_util_ColorCastDetExampleJni* Method: contrastImageColorCastOri* Signature: ([B[BIIIDD)Lcom/emp/empxmrz/util/ColorCastDetExampleJni/ContrastColorCastResults;*/
JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastOri(JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint, jint, jdouble, jdouble);/** Class: com_emp_empxmrz_util_ColorCastDetExampleJni* Method: contrastImageColorCastByPath* Signature: (Ljava/lang/String;Ljava/lang/String;IIIDD)Lcom/emp/empxmrz/util/ColorCastDetExampleJni/ContrastColorCastResults;*/
JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastByPath(JNIEnv *, jclass, jstring, jstring, jint, jint, jint, jdouble, jdouble);#ifdef __cplusplus
}
#endif
#endif
四、JNI 实现(C++)
这是 jni 的头文件实现,因为我是要调用多个算法,所以使用 vs 创建了一个解决方案,里面创建了多个项目,每个项目都是一种算法的 jni 层,根据自己的实际情况操作就可以,实现类中需要包含算法头文件和 jni 头文件,java实体的头文件不用添加。项目结构大致如下:
cpp 完整代码如下:
#include "ColorCast.h"
#include "com_emp_empxmrz_util_ColorCastDetExampleJni.h"#include <iostream>
#include <stdexcept>
#include <string>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>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);}
}void throwIllegalArgument(JNIEnv* env, const char* message) {jclass exClass = env->FindClass("java/lang/IllegalArgumentException");if (exClass != nullptr) {env->ThrowNew(exClass, message);}
}JNIEXPORT jint JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_getVersion(JNIEnv* env, jobject obj, jbyteArray buffer, jint bufSize) {try {jboolean isCopy = JNI_FALSE;jbyte* bufPtr = env->GetByteArrayElements(buffer, &isCopy);if (bufPtr == nullptr) {throwJavaException(env, "Failed to get buffer pointer");return 0;}// 写入版本号int len = ColorCastGetVersion(reinterpret_cast<char*>(bufPtr), static_cast<int>(bufSize));// 写成功,手动同步数据回 Javaenv->ReleaseByteArrayElements(buffer, bufPtr, 0);return len; }catch (const std::exception& e) {native_log(e.what());throwJavaException(env, e.what());return 0;}
}// 构造 ContrastColorCastResults Java 对象
jobject createContrastColorCastResults(JNIEnv* env, const ContrastColorCastResults& result) {jclass cls = env->FindClass("com/emp/empxmrz/util/ColorCastDetJni$ContrastColorCastResults");if (cls == nullptr) {throwJavaException(env, "Failed to find ContrastColorCastResults class");return nullptr;}jmethodID constructor = env->GetMethodID(cls, "<init>", "()V");if (constructor == nullptr) {throwJavaException(env, "Failed to get constructor for ContrastColorCastResults");return nullptr;}jobject obj = env->NewObject(cls, constructor);env->SetDoubleField(obj, env->GetFieldID(cls, "redContrast", "D"), result.Red_Contrast);env->SetDoubleField(obj, env->GetFieldID(cls, "greenContrast", "D"), result.Green_Contrast);env->SetDoubleField(obj, env->GetFieldID(cls, "yellowContrast", "D"), result.Yellow_Contrast);env->SetDoubleField(obj, env->GetFieldID(cls, "blueContrast", "D"), result.Blue_Contrast);env->SetDoubleField(obj, env->GetFieldID(cls, "colorCastRatio", "D"), result.ColorCast_ratio);return obj;
}JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCast
(JNIEnv* env, jobject obj,jbyteArray templateData, jint tw, jint th, jint tchannels,jbyteArray photoData, jint pw, jint ph, jint pchannels,jint cbmax, jint roiWidthDiv, jint roiHeightDiv,jdouble usWidthRatio, jdouble usHeightRatio) {try {jbyte* tData = env->GetByteArrayElements(templateData, nullptr);jbyte* pData = env->GetByteArrayElements(photoData, nullptr);ContrastColorCastResults result = contrastImageColorCast(reinterpret_cast<unsigned char*>(tData), tw, th, tchannels,reinterpret_cast<unsigned char*>(pData), pw, ph, pchannels,cbmax, roiWidthDiv, roiHeightDiv, usWidthRatio, usHeightRatio);env->ReleaseByteArrayElements(templateData, tData, 0);env->ReleaseByteArrayElements(photoData, pData, 0);// 将 ContrastColorCastResults 映射到 Java 对象return createContrastColorCastResults(env, result);}catch (const std::exception& e) {native_log(e.what());throwJavaException(env, e.what());return nullptr;}
}JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastOri
(JNIEnv* env, jobject obj,jbyteArray templateData, jbyteArray photoData,jint cbmax, jint roiWidthDiv, jint roiHeightDiv,jdouble usWidthRatio, jdouble usHeightRatio) {try {if (templateData == nullptr) {throwIllegalArgument(env,"templateData is null");return nullptr;}if (photoData == nullptr) {throwIllegalArgument(env, "photoData is null");return nullptr;}jsize tempLen = env->GetArrayLength(templateData);if (tempLen <= 0) {throwIllegalArgument(env, "templateData is null");return nullptr;}jsize photoLen = env->GetArrayLength(photoData);if (photoLen <= 0) {throwIllegalArgument(env, "photoData is null");return nullptr;}// Zero-copy: 获取 JVM 中的数据指针(映射)jbyte* tData = env->GetByteArrayElements(templateData, nullptr);if (tData == nullptr) {throwJavaException(env, "templateData Failed to get byte array elements");return nullptr;}// Zero-copy: 获取 JVM 中的数据指针(映射)jbyte* pData = env->GetByteArrayElements(photoData, nullptr);if (pData == nullptr) {throwJavaException(env, "photoData Failed to get byte array elements");return nullptr;}// 原图// 构造一个 OpenCV Mat 来包装 byte[] 数据(不复制)cv::Mat tBuf(1, tempLen, CV_8UC1, reinterpret_cast<uchar*>(tData));// 解码为图像cv::Mat tImage = cv::imdecode(tBuf, cv::IMREAD_UNCHANGED);if (tImage.empty()) {env->ReleaseByteArrayElements(photoData, pData, JNI_ABORT);throwJavaException(env, "templateData Failed to decode image");return nullptr;}// 目标图cv::Mat pBuf(1, photoLen, CV_8UC1, reinterpret_cast<uchar*>(pData));cv::Mat pImage = cv::imdecode(pBuf, cv::IMREAD_UNCHANGED);if (pImage.empty()) {env->ReleaseByteArrayElements(templateData, tData, JNI_ABORT);throwJavaException(env, "photoData Failed to decode image");return nullptr;}ContrastColorCastResults result = contrastImageColorCast(reinterpret_cast<unsigned char*>(tImage.data), tImage.cols, tImage.rows, tImage.channels(),reinterpret_cast<unsigned char*>(pImage.data), pImage.cols, pImage.rows, pImage.channels(),cbmax, roiWidthDiv, roiHeightDiv, usWidthRatio, usHeightRatio);// 释放映射,不拷贝回 Java(JNI_ABORT)env->ReleaseByteArrayElements(templateData, tData, JNI_ABORT);env->ReleaseByteArrayElements(photoData, pData, JNI_ABORT);// 将 ContrastColorCastResults 映射到 Java 对象return createContrastColorCastResults(env, result);}catch (const std::exception& e) {native_log(e.what());throwJavaException(env, e.what());return nullptr;}
}JNIEXPORT jobject JNICALL Java_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastByPath
(JNIEnv* env, jobject obj,jstring templateData, jstring photoData, jint cbmax, jint roiWidthDiv, jint roiHeightDiv,jdouble usWidthRatio, jdouble usHeightRatio) {try {const char* tImagePath = env->GetStringUTFChars(templateData, nullptr);const char* pImagePath = env->GetStringUTFChars(photoData, nullptr);// 直接用 OpenCV 读取图像cv::Mat tImage = cv::imread(tImagePath);if (tImage.empty()) {env->ReleaseStringUTFChars(templateData, tImagePath); // 释放资源env->ReleaseStringUTFChars(photoData, pImagePath);throwJavaException(env, "Failed to load templateData");return nullptr;}cv::Mat pImage = cv::imread(pImagePath);if (pImage.empty()) {env->ReleaseStringUTFChars(templateData, tImagePath); // 释放资源env->ReleaseStringUTFChars(photoData, pImagePath);throwJavaException(env, "Failed to load photoData");return nullptr;}ContrastColorCastResults result = contrastImageColorCast(reinterpret_cast<unsigned char*>(tImage.data), tImage.cols, tImage.rows, tImage.channels(),reinterpret_cast<unsigned char*>(pImage.data), pImage.cols, pImage.rows, pImage.channels(),cbmax, roiWidthDiv, roiHeightDiv, usWidthRatio, usHeightRatio);env->ReleaseStringUTFChars(templateData, tImagePath);env->ReleaseStringUTFChars(photoData, pImagePath);// 将 ContrastColorCastResults 映射到 Java 对象return createContrastColorCastResults(env, result);}catch (const std::exception& e) {native_log(e.what());throwJavaException(env, e.what());return nullptr;}
}
建议添加异常处理,避免JVM 崩溃,注意资源的释放。
五、Visual Studio 配置
1. 添加包含目录
打开【项目属性】 > C/C++
> 常规
> 附加包含目录
:
防止编译阶段报错。
2. 添加库目录
打开【链接器】 > 常规
> 附加库目录
:
3. 添加依赖库
打开【链接器】 > 输入
> 附加依赖项
:
告诉编译器如何调用 .dll
中的函数; 出现 JNI 方法未导出错误,可添加 .def
文件显式指定导出符号 ,内容如下,其实就是 jni 头文件方法:
LIBRARY ColorCastDetExampleJni
EXPORTSJava_com_emp_empxmrz_util_ColorCastDetExampleJni_getVersionJava_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastJava_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastOriJava_com_emp_empxmrz_util_ColorCastDetExampleJni_contrastImageColorCastByPath
4.生成 DLL
编译后会生成 ColorCastDetExampleJni.dll
和 ColorCastDetExampleJni.lib
,将他们复制到 jdk 的 bin 目录或者你自己定义的目录下, java 就可以直接调用 ColorCastDetExampleJni
了。
六、Java 调用测试
工具类 CustomImgUtils
和 ImageProcessor
在这篇文章里,Java 图像处理传 JNI 到 C++(OpenCV):两种高效实现方式对比
java 测试结果是否正确,也可以自己封装成接口。
service 层:
package com.emp.empxmrz.service;import com.emp.empxmrz.controller.vo.ColorCastDetExampleReq;
import com.emp.empxmrz.util.ColorCastDetExampleJni;
import com.emp.empxmrz.util.CustomImgUtils;
import com.emp.empxmrz.util.ImageProcessor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;/**** @title* @author shijiangyong* @date 2025/9/15 10:17**/
@Slf4j
@Service
public class ColorCastDetExampleJniServiceImpl implements ColorCastDetExampleJniService{@Overridepublic String getVersion() {byte[] versionBuffer = new byte[12];int result = ColorCastDetExampleJni.getVersion(versionBuffer, versionBuffer.length);String version = "获取版本号失败";if (result == 0) {version = new String(versionBuffer, StandardCharsets.UTF_8).trim();log.info("Algorithm model version number: {}", version);} else {log.warn("Failed to get version number: {} ", result);}return version;}@Overridepublic ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCast(ColorCastDetExampleReq req) {MultipartFile sourceFile = req.getSourceFile();if (sourceFile == null || sourceFile.isEmpty()) {throw new IllegalArgumentException("Source file is null or empty");}MultipartFile targetFile = req.getTargetFile();if (targetFile == null || targetFile.isEmpty()) {throw new IllegalArgumentException("Target file is null or empty");}try {byte[] sourceBytes = ImageProcessor.inputStreamToByteArray(sourceFile.getInputStream());BufferedImage sourceImage = ImageIO.read(new ByteArrayInputStream(sourceBytes));if (sourceImage == null) {throw new RuntimeException("图像读取失败");}byte[] targetBytes = ImageProcessor.inputStreamToByteArray(targetFile.getInputStream());BufferedImage targetImage = ImageIO.read(new ByteArrayInputStream(targetBytes));if (targetImage == null) {throw new RuntimeException("图像读取失败");}byte[] sourceMatrixBGR = CustomImgUtils.getMatrixBGR(sourceImage);byte[] targetMatrixBGR = CustomImgUtils.getMatrixBGR(targetImage);return ColorCastDetExampleJni.contrastImageColorCast(sourceMatrixBGR, sourceImage.getWidth(), sourceImage.getHeight(), sourceImage.getColorModel().getNumColorComponents(),targetMatrixBGR, targetImage.getWidth(), targetImage.getHeight(), targetImage.getColorModel().getNumColorComponents(),req.getYCrCbCbmax(), req.getRoiWidthDiv(), req.getRoiHeightDiv(), req.getUsWidthRatio(), req.getUsHeightRatio());} catch (IOException e) {throw new RuntimeException(e);}}@Overridepublic ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCastOri(ColorCastDetExampleReq req) {String sourceFileUrl = req.getSourceFileUrl();if (sourceFileUrl == null || sourceFileUrl.isEmpty()) {throw new IllegalArgumentException("Source file url is null or empty");}String targetFileUrl = req.getTargetFileUrl();if (targetFileUrl == null || targetFileUrl.isEmpty()) {throw new IllegalArgumentException("Target file url is null or empty");}try {byte[] sourceMatrixBGR = this.getImageBytes(sourceFileUrl);byte[] targetMatrixBGR = this.getImageBytes(targetFileUrl);return ColorCastDetExampleJni.contrastImageColorCastOri(sourceMatrixBGR, targetMatrixBGR,req.getYCrCbCbmax(), req.getRoiWidthDiv(), req.getRoiHeightDiv(), req.getUsWidthRatio(), req.getUsHeightRatio());} catch (IOException e) {throw new RuntimeException(e);}}@Overridepublic ColorCastDetExampleJni.ContrastColorCastResults contrastImageColorCastByPath(ColorCastDetExampleReq req) {String sourceFileUrl = "E:\\test\\CertifHaed.jpg";String targetFileUrl = "E:\\test\\face_big_homepage.jpg";return ColorCastDetExampleJni.contrastImageColorCastByPath(sourceFileUrl,targetFileUrl,req.getYCrCbCbmax(), req.getRoiWidthDiv(),req.getRoiHeightDiv(), req.getUsWidthRatio(), req.getUsHeightRatio());}/*** http流转BufferedImage* @param req* @return* @throws IOException*/private BufferedImage getBufferedImage(String req) throws IOException {URL url = new URL(req);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5000);conn.setReadTimeout(10000);return ImageIO.read(conn.getInputStream());}/*** http流转Byte[]* @param req* @return* @throws IOException*/private byte[] getImageBytes(String req) throws IOException {URL url = new URL(req);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5000);conn.setReadTimeout(10000);try (InputStream inputStream = conn.getInputStream()) {return ImageProcessor.inputStreamToByteArray(inputStream);}}
}
controller 层:
package com.emp.empxmrz.controller;import com.emp.empxmrz.controller.vo.ColorCastDetExampleReq;
import com.emp.empxmrz.service.ColorCastDetExampleJniService;
import com.emp.empxmrz.util.ColorCastDetExampleJni;
import com.emp.empxmrz.util.R;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/**** @title* @author shijiangyong* @date 2025/9/15 10:47**/
@Slf4j
@RestController
@AllArgsConstructor
@Tag(name = "demo示例")
@RequestMapping("/example")
public class ColorCastDetExampleJniController {private final ColorCastDetExampleJniService colorCastDetExampleJniService;@PostMapping("/getVersion")@Operation(summary = "管理后台-获取算法版本")public R<String> getVersion() {return R.ok(colorCastDetExampleJniService.getVersion());}@PostMapping("/contrast/color/castDet")@Operation(summary = "管理后台-偏色检测算法")public R<ColorCastDetExampleJni.ContrastColorCastResults> contrastImageColorCast(ColorCastDetExampleReq req) {return R.ok(colorCastDetExampleJniService.contrastImageColorCast(req));}@PostMapping("/contrast/color/castDetOri")@Operation(summary = "管理后台-偏色检测算法")public R<ColorCastDetExampleJni.ContrastColorCastResults> contrastImageColorCastOri(ColorCastDetExampleReq req) {return R.ok(colorCastDetExampleJniService.contrastImageColorCastOri(req));}@PostMapping("/contrast/color/castDetByPath")@Operation(summary = "管理后台-偏色检测算法")public R<ColorCastDetExampleJni.ContrastColorCastResults> contrastImageColorCastByPath(ColorCastDetExampleReq req) {return R.ok(colorCastDetExampleJniService.contrastImageColorCastByPath(req));}
}
调用示例:
路径去掉 Ori 就是测试上面两个入参,加上 Ori 就是测试下面两个入参,反正到了 java 层,就自己随便玩了。实体类就不贴了。
调试建议
- 如果报错找不到
ColorCastDetExampleJni.dll
,请将该 DLL 放入:- 项目运行目录;
- 或者
jdk/bin
目录; - 或者设置
-Djava.library.path
。
- 如果 JNI 函数名对应不上,请确保:
- 包名、类名、方法名匹配;
- DLL 导出的函数使用
extern "C"
。 - JNI 方法未正确导出,可通过
.def
文件显式指定导出符号。
七、总结
这里介绍 JNI 技术实现了 Java 与 C++ 的深度集成。整个流程分为:- 编写 Java native 接口;
- 使用
javah
生成 JNI 头文件; - C++ 实现 JNI 方法;
- 配置 Visual Studio 编译动态库;
- Java 调用测试。
如果你不想污染自己的JDK bin,可以将 .dll
和 .lib
放到一个统一的目录下,这样的话切换 jdk 比较方便,因为我只使用 jdk8,所以贪图方便放在了 bin 目录,但这是不规范滴 。