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



推荐阅读
  • 本文详细介绍了 `org.apache.tinkerpop.gremlin.structure.VertexProperty` 类中的 `key()` 方法,并提供了多个实际应用的代码示例。通过这些示例,读者可以更好地理解该方法在图数据库操作中的具体用途。 ... [详细]
  • 本文详细介绍了 Java 中 org.w3c.dom.Node 类的 isEqualNode() 方法的功能、参数及返回值,并通过多个实际代码示例来展示其具体应用。此方法用于检测两个节点是否相等,而不仅仅是判断它们是否为同一个对象。 ... [详细]
  • 一、使用Microsoft.Office.Interop.Excel.DLL需要安装Office代码如下:2publicstaticboolExportExcel(S ... [详细]
  • 最近遇到了一个关于单链表的编程问题,这是来自福富公司的笔试题目。以往我通常使用C语言来解决这类问题,但这次决定尝试用Java来实现。该题目要求实现一个单链表,并完成特定的方法。 ... [详细]
  • Java多线程售票案例分析
    本文通过一个售票系统的实例,深入探讨了Java中的多线程技术及其在资源共享和并发控制中的应用。售票过程涉及查询、收款、找零和出票等多个步骤,其中对总票数的管理尤为关键。 ... [详细]
  • 本文基于Java官方文档进行了适当修改,旨在介绍如何实现一个能够同时处理多个客户端请求的服务端程序。在前文中,我们探讨了单客户端访问的服务端实现,而本篇将深入讲解多客户端环境下的服务端设计与实现。 ... [详细]
  • 本文详细介绍了如何使用C#实现不同类型的系统服务账户(如Windows服务、计划任务和IIS应用池)的密码重置方法。 ... [详细]
  • 1、编写一个Java程序在屏幕上输出“你好!”。programmenameHelloworld.javapublicclassHelloworld{publicst ... [详细]
  • 本文详细探讨了在Java中如何将图像对象转换为文件和字节数组(Byte[])的技术。虽然网络上存在大量相关资料,但实际操作时仍需注意细节。本文通过使用JMSL 4.0库中的图表对象作为示例,提供了一种实用的方法。 ... [详细]
  • 处理Android EditText中数字输入与parseInt方法
    本文探讨了如何在Android应用中从EditText组件安全地获取并解析用户输入的数字,特别是用于设置端口号的情况。通过示例代码和异常处理策略,展示了有效的方法来避免因非法输入导致的应用崩溃。 ... [详细]
  • 二维码的实现与应用
    本文介绍了二维码的基本概念、分类及其优缺点,并详细描述了如何使用Java编程语言结合第三方库(如ZXing和qrcode.jar)来实现二维码的生成与解析。 ... [详细]
  • Beetl是一款先进的Java模板引擎,以其丰富的功能、直观的语法、卓越的性能和易于维护的特点著称。它不仅适用于高响应需求的大型网站,也适合功能复杂的CMS管理系统,提供了一种全新的模板开发体验。 ... [详细]
  • Gradle 是 Android Studio 中默认的构建工具,了解其基本配置对于开发效率的提升至关重要。本文将详细介绍如何在 Gradle 中定义和使用共享变量,以确保项目的一致性和可维护性。 ... [详细]
  • 设计一个算法,用于计算给定字符串中出现的不同ASCII字符数量。该任务将重点考察字符串处理、集合操作以及基础的输入输出技术。 ... [详细]
  • Flutter 核心技术与混合开发模式深入解析
    本文深入探讨了 Flutter 的核心技术,特别是其混合开发模式,包括统一管理模式和三端分离模式,以及混合栈原理。通过对比不同模式的优缺点,帮助开发者选择最适合项目的混合开发策略。 ... [详细]
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社区 版权所有