首页 > 其他分享 >JNA 加载动态链接库工具

JNA 加载动态链接库工具

时间:2023-02-07 11:55:21浏览次数:45  
标签:libName String lib HCNetSDKCom private JNA static 动态链接库 加载

原理:简而言之就是把jar包中的动态库解压写到系统临时文件目录中去,然后在动态库的目录结构的要求进行加载

获取系统临时文件目录:System.out.println(System.getProperty("java.io.tmpdir"));

// 如果动态库有目录结构方面的要求,就提前判断目录是否存在
public static void main(String[] args) {
    String HCNetSDKCom = System.getProperty("java.io.tmpdir") + "HCNetSDKCom";
    System.out.println(HCNetSDKCom);
    File file = new File(HCNetSDKCom);
    if (!file.exists()) {
        file.mkdir();
    }
}

【DynamicParseUtil】

package com.netsdk.lib.utils;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * 动态解析库
 */
public class DynamicParseUtil {
  private DynamicLibParseHandler handler;
  private SAXParserFactory saxParserFactory;
  private SAXParser saxParser;

  /**
   * 适配各系统动态库名称大小写不同,以及lib前缀造成的找不到库的问题
   *
   * @param currentSystem 当前系统:win64,win32,linux64,linux32,mac64
   * @param libName 动态库名称
   * @return
   */
  public String compareLibName(String currentSystem, String libName) {
    String dynamicLibName = libName;
    List<String> libs = handler.getLibsBySystem(currentSystem);
    if (currentSystem.equalsIgnoreCase("win64")) {
      return findLibs(libs, libName);
    }
    if (libName.startsWith("lib")) {
      dynamicLibName = libName.substring(3);
    }
    return findLibs(libs, dynamicLibName);
  }

  private String findLibs(List<String> libs, String libName) {
    for (String lib : libs) {
      if (libName.equalsIgnoreCase(lib)) {
        return lib;
      }
    }
    return "";
  }

  public List<String> getLibsSystem(String system) {
    return handler.getLibsBySystem(system);
  }

  private DynamicParseUtil() throws ParserConfigurationException {
    // 获取SAX分析器的工厂实例,专门负责创建SAXParser分析器
    saxParserFactory = SAXParserFactory.newInstance();
    // 获取SAXParser分析器的实例
    try {
      saxParser = saxParserFactory.newSAXParser();
      handler = new DynamicLibParseHandler();
    } catch (ParserConfigurationException | SAXException e) {
      throw new ParserConfigurationException();
    }
  }

  public DynamicParseUtil(InputStream inputSteam)
      throws ParserConfigurationException, IOException, SAXException {
    this();
    saxParser.parse(inputSteam, handler);
  }

  /** xml解析handler */
  private static class DynamicLibParseHandler extends DefaultHandler {
    private final HashMap<String, List<String>> dynamics = new HashMap<>();
    private final List<String> systems = Arrays.asList("win64", "win32", "linux64", "linux32", "mac64", "linuxARM");
    private String currentDynamicSystem;
    private List<String> libs;

    public List<String> getLibsBySystem(String system) {
      return dynamics.get(system);
    }

    @Override
    public void startDocument() throws SAXException {
      super.startDocument();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes)
        throws SAXException {
      super.startElement(uri, localName, qName, attributes);
      if (systems.contains(qName)) {
        currentDynamicSystem = qName;
        if (libs == null) {
          libs = new ArrayList<>();
        }
      }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
      super.endElement(uri, localName, qName);
      if (systems.contains(qName)) {
        // 保存到hashmap中
        dynamics.put(currentDynamicSystem, libs);
        // 清除libs
        libs = null;
      }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
      super.characters(ch, start, length);
      String lib = new String(ch, start, length);
      if (!lib.trim().isEmpty()) {
        libs.add(lib);
      }
    }
  }
}

【LibraryLoad】

package com.jt.hk.utils;


import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.*;

/**
 * @description 动态库加载
 */
public class LibraryLoad {
    private static final String ARCH_WINDOWS = "win";
    private static final String ARCH_LINUX = "linux";
    private static final String ARCH_MAC = "mac";
    private static final int PREFIX_64 = 64;
    private static final int PREFIX_32 = 32;
    private static final String PREFIX_ARM = "ARM";
    private static final String EXTERNAL_WIN = ".dll";
    private static final String EXTERNAL_LINUX = ".so";
    private static final String EXTERNAL_MAC = ".dylib";
    private static DynamicParseUtil dynamicParseUtil;
    /**
     * 当前读取的目录
     */
    private static String currentFold;
    /**
     * 动态库需要写入的目录
     */
    private static String EXTRACT_PATH = System.getProperty("java.io.tmpdir");

    private static boolean written = false;

    /**
     * 设置动态库写入的路径,适用于需要自定义加载路径的用户
     *
     * @param path 动态库写入的文件夹,从该文件夹下加载sdk的动态库
     */
    public static void setExtractPath(String path) {
        EXTRACT_PATH = path;
    }

    /**
     * 动态库路径
     */
    private static String INNER_PATH;

    // private static final String EXTERNAL_MAC = ".so";

    private static String extractNetSDKLib(String libName) {
        return extractLibrary(libName);
    }

    public static String getLoadLibrary(String libraryName) {
        currentFold = getLibraryFold();
        if (dynamicParseUtil == null) {
            try {
                dynamicParseUtil =
                        new DynamicParseUtil(
                                LibraryLoad.class.getClassLoader().getResourceAsStream("dynamic-lib-load.xml"));
                if (!written) {
                    for (String libName : dynamicParseUtil.getLibsSystem(currentFold)) {
                        extractLibrary(libName);
                    }
                    written = true;
                }
            } catch (ParserConfigurationException | IOException | SAXException e) {
                e.printStackTrace();
            }
        }
        String fullName = getLibraryName(libraryName);
        String path = EXTRACT_PATH;
        if (!(EXTRACT_PATH.endsWith("/") || EXTRACT_PATH.endsWith("\\"))) {
            path = EXTRACT_PATH + "/";
        }
        System.out.println("加载外部函数库: " + path + fullName);
        return path + fullName;
    }

    /**
     * 将jar包里的动态库写入到系统缓存目录,使用绝对路径加载动态库
     *
     * @param libName
     * @return
     */
    private static String extractLibrary(String libName) {
        return extractLibrary("", libName);
    }

    /**
     * 相对路径文件夹
     *
     * @param relativePath 相对路径
     * @param libName      动态库路径
     * @return
     */
    private static String extractLibrary(String relativePath, String libName) {
        if (libName.trim().equals("")) {
            return "";
        }
        String libFullName = getLibraryName(libName);
        String dir = getLibraryFold();
        if (!(relativePath.endsWith("/") || relativePath.endsWith("\\"))) {
            relativePath = relativePath + "/";
        }
        String fileName = relativePath + dir + "/" + libFullName;
        InputStream in = LibraryLoad.class.getResourceAsStream(fileName);
        BufferedInputStream reader;
        FileOutputStream writer;
        File extractedLibFile = null;
        try {
            if (in == null) {
                in = new FileInputStream(fileName);
                if (in == null) {
                    return "";
                }
            }
            String nativeTempDir = EXTRACT_PATH;
            if (!(nativeTempDir.endsWith("/") || nativeTempDir.endsWith("\\"))) {
                nativeTempDir = nativeTempDir + "/";
            }
            extractedLibFile = new File(nativeTempDir + libFullName);
            reader = new BufferedInputStream(in);
            writer = new FileOutputStream(extractedLibFile);
            byte[] buffer = new byte[1024];
            while (true) {
                int len = reader.read(buffer);
                if (len == 0 || len == -1) {
                    break;
                }
                writer.write(buffer, 0, len);
            }
            reader.close();
            writer.close();
            in.close();
        } catch (Exception e) {
            // System.out.println(
            //     "dynamic file[ "
            //         + fileName
            //         + " ] not found in project.please ensure you need this library.");
        }
        String res = extractedLibFile != null ? extractedLibFile.getAbsolutePath() : "";
        System.out.println("加载目标动态库的绝对路径为: >>>>>>> " + res);
        return res;
    }

    /**
     * 获取动态库完整名称
     *
     * @param libName
     * @return
     */
    private static String getLibraryName(String libName) {
        String dir = currentFold;
        String libPrefix = "";
        String libExtension = EXTERNAL_WIN;


        // 如果 dynamic-lib-load.xml 文件中写了扩展名,那么就不对扩展名做额外处理
        if (libName.contains(".")) {
            libExtension = "";
        }

        if (!dir.contains("win")) {
            libPrefix = "lib";
            if (dir.contains("linux")) {
                libExtension = EXTERNAL_LINUX;
            } else {
                // libExtension=".dylib";
                libExtension = EXTERNAL_MAC;
            }
        }
        libName = dynamicParseUtil.compareLibName(currentFold, libName);
        // 动态库以lib开头,则不添加lib前缀
        // 以lib开头的库则不添加lib前缀
        libName = (libName.startsWith("lib") ? "" : libPrefix) + libName + libExtension;
        System.out.println("获取 dynamic-lib-load.xml 中的 动态库名称为: >>>>>>> " + libName);
        return libName;
    }

    // 获取系统对应的动态库文件夹
    private static String getLibraryFold() {
        String osType;
        String osName = System.getProperty("os.name");
        if (osName.toLowerCase().startsWith("linux")) {
            osType = ARCH_LINUX;
        } else if (osName.toLowerCase().startsWith("mac")
                || osName.toLowerCase().startsWith("darwin")) {
            osType = ARCH_MAC;
        } else if (osName.toLowerCase().startsWith("windows")) {
            osType = ARCH_WINDOWS;
        } else {
            osType = "";
        }
        String arch = System.getProperty("os.arch");
        arch = arch.toLowerCase().trim();
        if ("i386".equals(arch) || "i686".equals(arch) || "x86".equals(arch)) {
            arch = PREFIX_32 + "";
        } else if ("x86_64".equals(arch) || "amd64".equals(arch)) {
            arch = PREFIX_64 + "";
        } else if (arch.startsWith("arm")) {
            arch = PREFIX_ARM + "";
        }
        System.out.println("获取系统对应平台及架构的对应匹配的动态库文件夹名称为: >>>>>>> " + osType + arch);
        return osType + arch;
    }
}

【dynamic-lib-load.xml】

<?xml version="1.0" encoding="UTF-8" ?>
<dynamic-lib>
    <win64> <!-- 这个是平台类型与架构,需要与resources目录保持一致才能正常匹配 -->
        <lib>AudioRender</lib>
        <lib>HCCore</lib>
        <lib>HCNetSDK</lib>
        <lib>hlog</lib>
        <lib>hpr</lib>
        <lib>libeay32</lib>
        <lib>PlayCtrl</lib>
        <lib>ssleay32</lib>
        <lib>SuperRender</lib>
        <lib>zlib1</lib>
        <!-- 带文件夹的写法 -->
        <lib>HCNetSDKCom/AnalyzeData.dll</lib>
        <lib>HCNetSDKCom/AudioIntercom.dll</lib>
        <lib>HCNetSDKCom/AudioRender.dll</lib>
        <lib>HCNetSDKCom/HCAlarm.dll</lib>
        <lib>HCNetSDKCom/HCAlarm.lib</lib>
        <lib>HCNetSDKCom/HCCoreDevCfg.dll</lib>
        <lib>HCNetSDKCom/HCDisplay.dll</lib>
        <lib>HCNetSDKCom/HCGeneralCfgMgr.dll</lib>
        <lib>HCNetSDKCom/HCGeneralCfgMgr.lib</lib>
        <lib>HCNetSDKCom/HCIndustry.dll</lib>
        <lib>HCNetSDKCom/HCPlayBack.dll</lib>
        <lib>HCNetSDKCom/HCPreview.dll</lib>
        <lib>HCNetSDKCom/HCPreview.lib</lib>
        <lib>HCNetSDKCom/HCVoiceTalk.dll</lib>
        <lib>HCNetSDKCom/libiconv2.dll</lib>
        <lib>HCNetSDKCom/OpenAL32.dll</lib>
        <lib>HCNetSDKCom/StreamTransClient.dll</lib>
        <lib>HCNetSDKCom/SystemTransform.dll</lib>
    </win64>
   
    <win32>
        <lib>avnetsdk</lib>
        <lib>dhconfigsdk</lib>
        <lib>dhnetsdk</lib>
        <lib>dhplay</lib>
        <lib>Infra</lib>
        <lib>ImageAlg</lib>
        <lib>StreamConvertor</lib>
        <lib>jninetsdk</lib>
    </win32>
    <linux64>
        <lib>avnetsdk</lib>
        <lib>dhnetsdk</lib>
        <lib>dhconfigsdk</lib>
        <lib>StreamConvertor</lib>
        <lib>jninetsdk</lib>
    </linux64>
    <linux32>
        <lib>avnetsdk</lib>
        <lib>dhconfigsdk</lib>
        <lib>dhnetsdk</lib>
        <lib>StreamConvertor</lib>
        <lib>jninetsdk</lib>
    </linux32>
    <mac64>
        <lib>avnetsdk</lib>
        <lib>dhnetsdk</lib>
        <lib>dhconfigsdk</lib>
        <lib>StreamConvertor</lib>
    </mac64>
</dynamic-lib>

使用

public interface NetSDKLib extends Library {

    NetSDKLib NETSDK_INSTANCE = Native.load(LibraryLoad.getLoadLibrary("dhnetsdk"), NetSDKLib.class);
     
    // 动态库的 jna 代码
    // ...
    // ...
}

标签:libName,String,lib,HCNetSDKCom,private,JNA,static,动态链接库,加载
From: https://www.cnblogs.com/hhddd-1024/p/17097895.html

相关文章

  • 8.8程序加载时会生成栈和堆
    EXE文件的内容分为再配置信息、变量组和函数组,这一点想必大家都清楚了吧。不过,当程序加载到内存后,除此之外还会额外生成两个组,那就是栈和堆。栈是用来存储函数内部临时使用......
  • IDEA加载项目时提示Windows Defender might be impacting your build performance
    1.点击fix,弹出对话框,选择configureautomatically,选择ok2.出现以下提示表示成功 3.若不成功,可以手动设置防火墙白名单,添加排除项 ......
  • QML中文件的加载(三种方法)
    篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QML中文件的加载(三种方法)相关的知识,希望对你有一定的参考价值。在这里小小总结一下QML文件中如何加载QML文件......
  • QML文件加载的几种方式
    整理下QML过程,看到好多方式加载QML文件,这里参考网上总结整理下.QQmlApplicationEngine加载QML查看帮助文档,可以看出QQmlApplicationEngine继承自QQmlEngine->QObject.......
  • Qt加载qml的方式
    1、QQmlApplicationEngined搭配Window示例:#include<QGuiApplication>#include<QQmlApplicationEngine>intmain(intargc,char*argv[]){QGuiApplication......
  • 面试被问到“类的加载过程”,怎么回答可以脱颖而出?
    大家好,我是哪吒。一、做一个小测试,通过注释,标注出下面两个类中每个方法的执行顺序,并写出studentId的最终值。packagecom.nezha.javase;publicclassPerson1{p......
  • 面试被问到“类的加载过程”,怎么回答可以脱颖而出?
    大家好,我是哪吒。一、做一个小测试,通过注释,标注出下面两个类中每个方法的执行顺序,并写出studentId的最终值。packagecom.nezha.javase;publicclassPerson1{private......
  • JAVA 双亲委派与类加载器
    JAVA双亲委派与类加载器双亲委派虚拟机在加载类的过程中需要使用类加载器进行加载,而在Java中,类加载器有很多,那么当JVM想要加载一个.class文件的时候,到底应该由哪个类加......
  • [Oracle19C 数据库管理] 加载和传输数据库
    移动数据的通用架构数据泵datapump(impdp,expdp),借助DBMS_DATAPUMP存储过程,可以进行表的导出导入,行记录的导出导入,表空间的导出导入或者整个schema的导出导入。使用数......
  • 6.7【微信小程序全栈开发课程】记录页面(七)--分页加载记录数据
    现在是一次性加载所有的记录数据,数据多的时候,会加载比较慢,所以我们改成分页加载,一次最多加载15条数据每次拉倒底部都会自动加载下一页的数据,知道所有的数据加载完成1、添加d......