本文不是关于如何创建调用 C/C++ API 的 Java 本机方法。它旨在帮助开发人员构建一个包含 JNI 共享库的 jar 包,使用Dynamsoft Barcode Reader制作,适用于Windows、Linux和macOS从头开始。我将演示如何使用CMake创建 JNI 共享库,以及如何使用Eclipse和Maven将类文件和共享库打包成 jar 文件。
如何将 Maven 与 Dynamsoft 条码阅读器一起使用
Dynamsoft 提供了完整的 Java 开发包。您可以将它与 Maven 一起使用,如下所示:
<repositories>
<repository>
<id>dbr</id>
<url>https://download./maven/dbr/jar</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.dynamsoft</groupId>
<artifactId>dbr</artifactId>
<version>6.2</version>
</dependency>
</dependencies>
如果你对如何一步步构建这样的 jar 包不感兴趣,可以忽略以下段落。
如何在 Java 中定义本机方法
在 Eclipse 中创建一个新的 Maven 项目。
创建一个 Java 类NativeBarcodeReader.java:
public class NativeBarcodeReader {
private long nativePtr = 0;
static {
if (System.getProperty("java.vm.vendor").contains("Android")) {
System.loadLibrary("dbr");
} else {
try {
if (NativeLoader.load()) {
System.out.println("Successfully loaded Dynamsoft Barcode Reader.");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public NativeBarcodeReader() {
nativePtr = nativeCreateInstance();
}
public void destroyInstance() {
if (nativePtr != 0)
nativeDestroyInstance(nativePtr);
}
public void setLicense(String license) {
nativeInitLicense(nativePtr, license);
}
public void decodeFile(String fileName) {
nativeDecodeFile(nativePtr, fileName);
}
private native int nativeInitLicense(long nativePtr, String license);
private native long nativeCreateInstance();
private native void nativeDestroyInstance(long nativePtr);
private native void nativeDecodeFile(long nativePtr, String fileName);
}
注意:如果使用System.load() 方法,则必须在加载 JNI 共享库之前加载所有依赖项。
Eclipse 会自动将源代码编译成一个类文件。使用javah从jni文件夹中的类生成 C 头NativeBarcodeReader.h 。
mkdir jni
cd jni
javah -cp ..\target\classes -o NativeBarcodeReader.h com.dynamsoft.barcode.NativeBarcodeReader
创建对应的NativeBarcodeReader.cxx文件:
#include "NativeBarcodeReader.h"
#include "DynamsoftBarcodeReader.h"
#ifdef __cplusplus
extern "C"
{
#endif
/*
* Class: com_dynamsoft_barcode_NativeBarcodeReader
* Method: nativeInitLicense
* Signature: (JLjava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_dynamsoft_barcode_NativeBarcodeReader_nativeInitLicense(JNIEnv *env, jobject, jlong hBarcode, jstring license)
{
const char *pszLicense = env->GetStringUTFChars(license, NULL);
if (hBarcode)
{
DBR_InitLicense((void *)hBarcode, pszLicense);
}
env->ReleaseStringUTFChars(license, pszLicense);
return 0;
}
/*
* Class: com_dynamsoft_barcode_NativeBarcodeReader
* Method: nativeCreateInstance
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_com_dynamsoft_barcode_NativeBarcodeReader_nativeCreateInstance(JNIEnv *, jobject)
{
return (jlong)DBR_CreateInstance();
}
/*
* Class: com_dynamsoft_barcode_NativeBarcodeReader
* Method: nativeDestroyInstance
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_com_dynamsoft_barcode_NativeBarcodeReader_nativeDestroyInstance(JNIEnv *, jobject, jlong hBarcode)
{
if (hBarcode)
{
DBR_DestroyInstance((void *)hBarcode);
}
}
/*
* Class: com_dynamsoft_barcode_NativeBarcodeReader
* Method: nativeDecodeFile
* Signature: (JLjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_dynamsoft_barcode_NativeBarcodeReader_nativeDecodeFile(JNIEnv *env, jobject, jlong ptr, jstring fileName)
{
if (ptr)
{
void *hBarcode = (void *)ptr;
const char *pszFileName = env->GetStringUTFChars(fileName, NULL);
DBR_DecodeFile(hBarcode, pszFileName, "");
STextResultArray *paryResult = NULL;
DBR_GetAllTextResults(hBarcode, &paryResult);
int count = paryResult->nResultsCount;
int i = 0;
for (; i < count; i++)
{
printf("Index: %d, Type: %s, Value: %s\n", i, paryResult->ppResults[i]->pszBarcodeFormatString, paryResult->ppResults[i]->pszBarcodeText); // Add results to list
}
// Release memory
DBR_FreeTextResults(&paryResult);
env->ReleaseStringUTFChars(fileName, pszFileName);
}
}
#ifdef __cplusplus
}
#endif
这里的代码非常简单。如果您愿意,可以添加更多实现。
SDK
下载 适用于 Windows、Linux 和 macOS 的 Dynamsoft Barcode Reader。
将依赖于操作系统的共享库复制到 jni/platforms 文件夹。
- jni/平台/赢
DBRx64.lib
DynamicPdfx64.dll
DynamsoftBarcodeReaderx64.dll
vcomp110.dll
- jni/平台/linux
libDynamicPdf.so
libDynamsoftBarcodeReader.so
- jni/平台/macos
libDynamsoftBarcodeReader.dylib
如何使用 CMake 构建 JNI 共享库
创建一个CMakeLists.txt文件。
定义 Java 包含和 lib 目录:
if (CMAKE_HOST_WIN32)
set(WINDOWS 1)
set(JAVA_INCLUDE "C:/Program Files/Java/jdk1.8.0_181/include")
set(JAVA_INCLUDE_OS "C:/Program Files/Java/jdk1.8.0_181/include/win32")
elseif(CMAKE_HOST_APPLE)
set(MACOS 1)
set(JAVA_INCLUDE "/System/Library/Frameworks/JavaVM.framework/Headers")
set(JAVA_INCLUDE_OS "")
elseif(CMAKE_HOST_UNIX)
set(LINUX 1)
set(JAVA_INCLUDE "/usr/lib/jvm/java-1.8.0-openjdk-amd64/include/")
set(JAVA_INCLUDE_OS "/usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux")
endif()
if(WINDOWS)
link_directories("${PROJECT_SOURCE_DIR}/platforms/win" "C:/Program Files/Java/jdk1.8.0_181/lib")
include_directories("${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/include" "${PROJECT_SOURCE_DIR}" "${JAVA_INCLUDE}" "${JAVA_INCLUDE_OS}")
elseif(LINUX)
link_directories("${PROJECT_SOURCE_DIR}/platforms/linux")
include_directories("${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/include" "${PROJECT_SOURCE_DIR}" "${JAVA_INCLUDE}" "${JAVA_INCLUDE_OS}")
elseif(MACOS)
link_directories("${PROJECT_SOURCE_DIR}/platforms/macos")
include_directories("${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/include" "${PROJECT_SOURCE_DIR}" "${JAVA_INCLUDE}")
endif()
链接 Dynamsoft Barcode Reader 以生成不同平台的 JNI 共享库。对于 macOS ,将文件后缀从 dylib 重命名为jnilib:
add_library(dbr SHARED NativeBarcodeReader.cxx)
if(MACOS)
set_target_properties(dbr PROPERTIES SUFFIX ".jnilib")
endif()
if(WINDOWS)
if(CMAKE_CL_64)
target_link_libraries (dbr "DBRx64")
else()
target_link_libraries (dbr "DBRx86")
endif()
else()
target_link_libraries (dbr "DynamsoftBarcodeReader")
endif()
安装库:
set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/../src/main/")
set(ECLIPSE_PATH "java/com/dynamsoft/barcode/native")
set(MAVEN_PATH "resources/com/dynamsoft/barcode/native")
if(WINDOWS)
install (DIRECTORY "${PROJECT_SOURCE_DIR}/platforms/win" DESTINATION "${CMAKE_INSTALL_PREFIX}${ECLIPSE_PATH}")
install (DIRECTORY "${PROJECT_SOURCE_DIR}/platforms/win" DESTINATION "${CMAKE_INSTALL_PREFIX}${MAVEN_PATH}")
install (TARGETS dbr DESTINATION "${CMAKE_INSTALL_PREFIX}${ECLIPSE_PATH}/win")
install (TARGETS dbr DESTINATION "${CMAKE_INSTALL_PREFIX}${MAVEN_PATH}/win")
elseif(LINUX)
install (DIRECTORY "${PROJECT_SOURCE_DIR}/platforms/linux" DESTINATION "${CMAKE_INSTALL_PREFIX}${ECLIPSE_PATH}")
install (DIRECTORY "${PROJECT_SOURCE_DIR}/platforms/linux" DESTINATION "${CMAKE_INSTALL_PREFIX}${MAVEN_PATH}")
install (TARGETS dbr DESTINATION "${CMAKE_INSTALL_PREFIX}${ECLIPSE_PATH}/linux")
install (TARGETS dbr DESTINATION "${CMAKE_INSTALL_PREFIX}${MAVEN_PATH}/linux")
elseif(MACOS)
install (DIRECTORY "${PROJECT_SOURCE_DIR}/platforms/macos" DESTINATION "${CMAKE_INSTALL_PREFIX}${ECLIPSE_PATH}")
install (DIRECTORY "${PROJECT_SOURCE_DIR}/platforms/macos" DESTINATION "${CMAKE_INSTALL_PREFIX}${MAVEN_PATH}")
install (TARGETS dbr DESTINATION "${CMAKE_INSTALL_PREFIX}${ECLIPSE_PATH}/macos")
install (TARGETS dbr DESTINATION "${CMAKE_INSTALL_PREFIX}${MAVEN_PATH}/macos")
endif()
ECLIPSE_PATH 用于 Eclipse 导出 jar 文件,而 MAVEN_PATH用于Maven 导出 jar 文件。
构建 JNI 共享库。
视窗
例如,Visual Studio 2017
mkdir build
cd build
cmake -G"Visual Studio 15 2017 Win64" ..
cmake --build . --config Release --target install
Linux 和 macOS
mkdir build
cd build
cmake ..
cmake --build . --config Release --target install
如何将原生库打包成 Jar 文件
编辑 pom.xml 文件,添加共享库目录:
<project xmlns="http://maven./POM/4.0.0"
xmlns:xsi="http://www./2001/XMLSchema-instance" xsi:schemaLocation="http://maven./POM/4.0.0 http://maven./xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dynamsoft</groupId>
<artifactId>barcode</artifactId>
<version>1.0.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>**/*.md</exclude>
<exclude>**/*.h</exclude>
<exclude>**/*.lib</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
在目标文件夹中生成jar文件:
或者,您可以使用 Eclipse 导出 jar 文件。
这是生成的jar文件。
如何从 jar 文件中加载 JNI 共享库
列出所有依赖库文件:
String[] filenames = null;
if (Utils.isWindows()) {
filenames = new String[] {"vcomp110.dll", "DynamicPdfx64.dll", "DynamsoftBarcodeReaderx64.dll", "dbr.dll"};
}
else if (Utils.isLinux()) {
filenames = new String[] {"libDynamicPdf.so", "libDynamsoftBarcodeReader.so", "libdbr.so"};
}
else if (Utils.isMac()) {
filenames = new String[] {"libDynamsoftBarcodeReader.dylib", "libdbr.jnilib"};
}
boolean ret = true;
for (String file : filenames) {
ret &= extractAndLoadLibraryFile(dbrNativeLibraryPath, file, tempFolder);
}
将共享库提取到临时文件夹:
private static boolean extractAndLoadLibraryFile(String libFolderForCurrentOS, String libraryFileName,
String targetFolder) {
String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName;
String extractedLibFileName = libraryFileName;
File extractedLibFile = new File(targetFolder, extractedLibFileName);
try {
if (extractedLibFile.exists()) {
// test md5sum value
String md5sum1 = md5sum(NativeBarcodeReader.class.getResourceAsStream(nativeLibraryFilePath));
String md5sum2 = md5sum(new FileInputStream(extractedLibFile));
if (md5sum1.equals(md5sum2)) {
return loadNativeLibrary(targetFolder, extractedLibFileName);
} else {
// remove old native library file
boolean deletionSucceeded = extractedLibFile.delete();
if (!deletionSucceeded) {
throw new IOException(
"failed to remove existing native library file: " + extractedLibFile.getAbsolutePath());
}
}
}
// Extract file into the current directory
InputStream reader = NativeBarcodeReader.class.getResourceAsStream(nativeLibraryFilePath);
FileOutputStream writer = new FileOutputStream(extractedLibFile);
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, bytesRead);
}
writer.close();
reader.close();
if (!System.getProperty("os.name").contains("Windows")) {
try {
Runtime.getRuntime().exec(new String[] { "chmod", "755", extractedLibFile.getAbsolutePath() })
.waitFor();
} catch (Throwable e) {
}
}
return loadNativeLibrary(targetFolder, extractedLibFileName);
} catch (IOException e) {
System.err.println(e.getMessage());
return false;
}
}
使用绝对路径加载库:
private static synchronized boolean loadNativeLibrary(String path, String name) {
File libPath = new File(path, name);
if (libPath.exists()) {
try {
System.load(new File(path, name).getAbsolutePath());
return true;
} catch (UnsatisfiedLinkError e) {
System.err.println(e);
return false;
}
} else
return false;
}
运行测试:
java -cp ./target/barcode-1.0.0.jar com.dynamsoft.barcode.Test
参考
源代码
https://github.com/dynamsoft-dbr/java-jni-barcode
|