热门标签 | 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



推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • Android 渐变圆环加载控件实现
    本文介绍了如何在 Android 中创建一个自定义的渐变圆环加载控件,该控件已在多个知名应用中使用。我们将详细探讨其工作原理和实现方法。 ... [详细]
  • 本文详细探讨了JDBC(Java数据库连接)的内部机制,重点分析其作为服务提供者接口(SPI)框架的应用。通过类图和代码示例,展示了JDBC如何注册驱动程序、建立数据库连接以及执行SQL查询的过程。 ... [详细]
  • 本文详细探讨了Java中的ClassLoader类加载器的工作原理,包括其如何将class文件加载至JVM中,以及JVM启动时的动态加载策略。文章还介绍了JVM内置的三种类加载器及其工作方式,并解释了类加载器的继承关系和双亲委托机制。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • Java 类成员初始化顺序与数组创建
    本文探讨了Java中类成员的初始化顺序、静态引入、可变参数以及finalize方法的应用。通过具体的代码示例,详细解释了这些概念及其在实际编程中的使用。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文详细分析了Hive在启动过程中遇到的权限拒绝错误,并提供了多种解决方案,包括调整文件权限、用户组设置以及环境变量配置等。 ... [详细]
  • 从 .NET 转 Java 的自学之路:IO 流基础篇
    本文详细介绍了 Java 中的 IO 流,包括字节流和字符流的基本概念及其操作方式。探讨了如何处理不同类型的文件数据,并结合编码机制确保字符数据的正确读写。同时,文中还涵盖了装饰设计模式的应用,以及多种常见的 IO 操作实例。 ... [详细]
  • 解决JAX-WS动态客户端工厂弃用问题并迁移到XFire
    在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ... [详细]
  • 深入理解Shell脚本编程
    本文详细介绍了Shell脚本编程的基础概念、语法结构及其在操作系统中的应用。通过具体的示例代码,帮助读者掌握如何编写和执行Shell脚本。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • Spring Boot 中静态资源映射详解
    本文深入探讨了 Spring Boot 如何简化 Web 应用中的静态资源管理,包括默认的静态资源映射规则、WebJars 的使用以及静态首页的处理方法。通过本文,您将了解如何高效地管理和引用静态资源。 ... [详细]
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社区 版权所有