分享

How to Package JNI Shared Library into Jar File | Dynamsoft Developers

 oskycar 2022-04-18

2018 年 9 月 25 日

本文不是关于如何创建调用 C/C++ API 的 Java 本机方法。它旨在帮助开发人员构建一个包含 JNI 共享库的 jar 包,使用Dynamsoft Barcode Reader制作,适用于WindowsLinuxmacOS从头开始​​。我将演示如何使用CMake创建 JNI 共享库,以及如何使用EclipseMaven将类文件和共享库打包成 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 项目。

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文件:

mvn package

Maven JNI 共享库

或者,您可以使用 Eclipse 导出 jar 文件。

Eclipse 导出 JNI 共享库

这是生成的jar文件。

java jni 罐子

如何从 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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多