本文不是關于如何創(chuàng)建調用 C/C++ API 的 Java 本機方法。它旨在幫助開發(fā)人員構建一個包含 JNI 共享庫的 jar 包,使用Dynamsoft Barcode Reader制作,適用于Windows、Linux和macOS從頭開始??。我將演示如何使用CMake創(chuàng)建 JNI 共享庫,以及如何使用Eclipse和Maven將類文件和共享庫打包成 jar 文件。
Dynamsoft 提供了完整的 Java 開發(fā)包。您可以將它與 Maven 一起使用,如下所示:
<repositories>
<repository>
<id>dbr</id>
<url>https://download.dynamsoft.com/maven/dbr/jar</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.dynamsoft</groupId>
<artifactId>dbr</artifactId>
<version>6.2</version>
</dependency>
</dependencies>
如果你對如何一步步構建這樣的 jar 包不感興趣,可以忽略以下段落。
在 Eclipse 中創(chuàng)建一個新的 Maven 項目。
創(chuàng)建一個 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
創(chuàng)建對應的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
這里的代碼非常簡單。如果您愿意,可以添加更多實現(xiàn)。
下載 適用于 Windows、Linux 和 macOS 的 Dynamsoft Barcode Reader。
將依賴于操作系統(tǒng)的共享庫復制到 jni/platforms
文件夾。
DBRx64.lib
DynamicPdfx64.dll
DynamsoftBarcodeReaderx64.dll
vcomp110.dll
libDynamicPdf.so
libDynamsoftBarcodeReader.so
libDynamsoftBarcodeReader.dylib
創(chuàng)建一個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
編輯 pom.xml 文件,添加共享庫目錄:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/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文件:
mvn package
或者,您可以使用 Eclipse 導出 jar 文件。
這是生成的jar文件。
列出所有依賴庫文件:
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