博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何把Windows,Linux和macOS的动态链接库封装到一个Java Jar包中
阅读量:6153 次
发布时间:2019-06-21

本文共 11867 字,大约阅读时间需要 39 分钟。

hot3.png

通过JNI,我们可以让Java调用C/C++的库。C/C++的库是平台相关的。要让依赖JNI动态链接库的Java开发包跨平台,需要把各个平台的库都封装到一个Jar包里。这篇文章分享下如何基于,用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 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);}

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

mkdir jnicd jnijavah -cp ..\target\classes -o NativeBarcodeReader.h com.dynamsoft.barcode.NativeBarcodeReader

创建对应的C/C++文件NativeBarcodeReader.cxx:

#include "NativeBarcodeReader.h"#include "DynamsoftBarcodeReader.h" #ifdef __cplusplusextern "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

下载 .

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

  • 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 buildcd buildcmake -G"Visual Studio 15 2017 Win64" .. cmake --build . --config Release --target install

Linux & macOS

mkdir buildcd buildcmake .. cmake --build . --config Release --target install

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

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

4.0.0
com.dynamsoft
barcode
1.0.0
UTF-8
src/main/resources
**/*.md
**/*.h
**/*.lib
org.apache.maven.plugins
maven-compiler-plugin
2.5.1
1.7
1.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 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://my.oschina.net/yushulx/blog/2209522

你可能感兴趣的文章
关于JavaScript词法
查看>>
FreeSwitch中的会议功能(4)
查看>>
MySQL中创建用户分配权限(到指定数据库或者指定数据库表中)
查看>>
AutoReleasePool 和 ARC 以及Garbage Collection
查看>>
重新想象 Windows 8 Store Apps (9) - 控件之 ScrollViewer 基础
查看>>
乐在其中设计模式(C#) - 提供者模式(Provider Pattern)
查看>>
MVP Community Camp 社区大课堂
查看>>
GWT用frame调用JSP
查看>>
大型高性能ASP.NET系统架构设计
查看>>
insert select带来的问题
查看>>
EasyUI 添加tab页(iframe方式)
查看>>
mysqldump主要参数探究
查看>>
好记心不如烂笔头,ssh登录 The authenticity of host 192.168.0.xxx can't be established. 的问题...
查看>>
使用addChildViewController手动控制UIViewController的切换
查看>>
Android Fragment应用实战
查看>>
SQL Server查询死锁并KILL
查看>>
内存或磁盘空间不足,Microsoft Office Excel 无法再次打开或保存任何文档。 [问题点数:20分,结帖人wenyang2004]...
查看>>
委托到Lambda的进化: ()=> {} 这个lambda表达式就是一个无参数的委托及具体方法的组合体。...
查看>>
apache 伪静态 .htaccess
查看>>
unity3d 截屏
查看>>