热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何把Windows,Linux和macOS的动态链接库封装到一个JavaJar包中

2019独角兽企业重金招聘Python工程师标准通过JNI,我们可以让Java调用CC的库。CC的库是平台相关的。要让依赖JNI动态链接库的Java开发包跨平台

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

通过JNI,我们可以让Java调用C/C++的库。C/C++的库是平台相关的。要让依赖JNI动态链接库的Java开发包跨平台,需要把各个平台的库都封装到一个Jar包里。这篇文章分享下如何基于Dynamsoft Barcode Reader,用CMake为Windows,Linux和macOS快速构建JNI动态链接库,以及如何用Maven把.class,.dll,.dylib,.so文件打包到Jar包中。

创建Java类,生成.h头文件

用Eclipse创建一个Maven工程:

创建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 blocke.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);
}

Eclipse会自动编译.class文件到target目录中。使用javah命令生成头文件:

mkdir jni
cd jni
javah -cp ..\target\classes -o NativeBarcodeReader.h com.dynamsoft.barcode.NativeBarcodeReader

创建对应的C/C++文件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 ppResults[i]->pszBarcodeFormatString, paryResult->ppResults[i]->pszBarcodeText); // Add results to list}// Release memoryDBR_FreeTextResults(&paryResult);env->ReleaseStringUTFChars(fileName, pszFileName);}}#ifdef __cplusplus
}
#endif

SDK

下载 Dynamsoft Barcode Reader for Windows, Linux and macOS.

把动态链接库拷贝到对应的目录下即可.

  • jni/platforms/win

    • DBRx64.lib
    • DynamicPdfx64.dll
    • DynamsoftBarcodeReaderx64.dll
    • vcomp110.dll
  • jni/platforms/linux

    • libDynamicPdf.so
    • libDynamsoftBarcodeReader.so
  • jni/platforms/macos

    • libDynamsoftBarcodeReader.dylib

用CMake编译JNI动态链接库

创建CMakeLists.txt. 定义头文件和库的路径:

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()

指定动态链接库的名字以及依赖的库。针对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()

把生成的库拷贝到指定目录中。这里针对Eclipse和Maven的需要拷贝了两次。最后生成结果里只有一份拷贝。

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()

编译JNI动态链接库。

Windows

E.g., 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

如何把动态链接库和class文件打包到Jar包中

编辑pom.xml指定动态链接库的路径:

4.0.0com.dynamsoftbarcode1.0.0UTF-8src/main/resources**/*.md**/*.h**/*.liborg.apache.maven.pluginsmaven-compiler-plugin2.5.11.71.7

用Maven打包:

mvn package

也可以用Eclipse导出:

最后生成的Jar包结构:

如何加载Jar包中的动态链接库

动态链接库在Jar包中是不能直接加载的,需要先解压到临时目录中。再通过System.load()指定绝对路径来加载。

所有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 valueString 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 fileboolean deletionSucceeded = extractedLibFile.delete();if (!deletionSucceeded) {throw new IOException("failed to remove existing native library file: " + extractedLibFile.getAbsolutePath());}}}// Extract file into the current directoryInputStream 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;}} elsereturn false;}

运行程序:

java -cp ./target/barcode-1.0.0.jar com.dynamsoft.barcode.Test

源码

https://github.com/dynamsoft-dbr/java-jni-barcode


转:https://my.oschina.net/yushulx/blog/2209522



推荐阅读
  • Java 中 Writer flush()方法,示例 ... [详细]
  • 本文介绍了如何使用 Spring Boot DevTools 实现应用程序在开发过程中自动重启。这一特性显著提高了开发效率,特别是在集成开发环境(IDE)中工作时,能够提供快速的反馈循环。默认情况下,DevTools 会监控类路径上的文件变化,并根据需要触发应用重启。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • 本文探讨了在Java中实现系统托盘最小化的两种方法:使用SWT库和JDK6自带的功能。通过这两种方式,开发者可以创建跨平台的应用程序,使窗口能够最小化到系统托盘,并提供丰富的交互功能。 ... [详细]
  • 本文总结了Java程序设计第一周的学习内容,涵盖语言基础、编译解释过程及基本数据类型等核心知识点。 ... [详细]
  • 本文详细介绍了如何正确安装Java EE SDK,并解决在安装过程中可能遇到的问题,特别是关于servlet代码在Apache Tomcat 10中无法运行的情况。 ... [详细]
  • Java 中的 BigDecimal pow()方法,示例 ... [详细]
  • Java 类成员初始化顺序与数组创建
    本文探讨了Java中类成员的初始化顺序、静态引入、可变参数以及finalize方法的应用。通过具体的代码示例,详细解释了这些概念及其在实际编程中的使用。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 主要用了2个类来实现的,话不多说,直接看运行结果,然后在奉上源代码1.Index.javaimportjava.awt.Color;im ... [详细]
  • 本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • Struts与Spring框架的集成指南
    本文详细介绍了如何将Struts和Spring两个流行的Java Web开发框架进行整合,涵盖从环境配置到代码实现的具体步骤。 ... [详细]
author-avatar
潘泓浩_236
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有