IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java使用JNI调用动态链接库(Mac版) -> 正文阅读

[Java知识库]Java使用JNI调用动态链接库(Mac版)

? 最近在学习C/C++,算是刚刚入门,之前一直从事Java开发,一直就听过看过JNI的一些使用,但是一直没动手实验过,所以这次想借着这个机会实现一下,让我的知识连贯起来,但是果真一看就会一动就废,尤其是我的电脑还是Mac的M1芯片,在整个JNI的实现过程可谓是一波三折。

1.前置知识

? 在使用JNI之前我想先介绍一下什么是链接以及什么是静态链接和动态链接(没有学过C/C++可以先跳过),所谓静态链接就是链接静态库的过程,动态链接就是链接动态库的过程,那么什么是静态库和动态库呢?他俩其实是一个相对的概念,静态库就是在编译阶段就可以和汇编生成的目标文件一起链接成为可执行文件。动态库则是在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。由上边的概念就可以看出两者的优缺点:静态库的优点就是随着编译和目标文件一起成为可执行文件,这样的方式移植性会高一些,但是缺点也是很明显的,就是它比较占用内存。动态库的优点就是只会在运行期间动态加载,需要什么,加载什么,并且在内存中只有一份,极大的减少了内存空间。

? 在不同系统中,动态库和静态库的命名方式是不同:

? (1) .dll .obj .lib使用在windows平台下。

? .dll:动态链接库,作为共享函数库的可执行文件。 .obj:目标文件,相当于源代码对应的二进制文件,未经重定位。 .lib:可理解为多个obj的集合,本质与.obj相同。(静态连接库)

? (2) .so .o .a使用在linux平台下。

? .so:(share object)动态链接库,跟Windows平台类似。 .o: 目标文件,相当于源代码对应的二进制文件。 .a: 与.o类似,多个.o的集合。(静态连接库)

? (3) macOS系统的动态库则使用.dylib作为动态库后缀。静态库与Linux相同

2.Java中的JNI

? 有了上边简单的动态链接库和静态链接库的知识,我们知道,静态链接库一般在编译期间就确定了,动态链接库生成以后可以供C/C++调用,但是Java开发者在开发初期就为我们预留了调用动态链接库的借口-JNI,使用JNI我们可以更加方便的调用C/C++生成的动态链接库,以此来实现一些使用C/C++开发的优秀的代码。

准备环境:

? Macos(我的是M1芯片),Gcc编译器(M1版本),Java1.8版本(OpenJdk_Zulu)。

开始演示:

我的所有操作都在一个目录下,首先准备一段Java代码如下:

/**
 * 我没有定义包路径
 */
public class TestDib {
  
    public native void hello();
  
    static {
        //查找路径为当前项目路径
        System.setProperty("java.library.path", ".");
        //加载动态库的名称
        System.loadLibrary("HelloWorld");
    }
  
    public static void main(String[] args){
        new TestDib().hello();
    }
  
}

上述代码的native方法可以拆出来单独放在一个类中,这里为了方便我就都写在一起了,同时为了演示方便就没有定义包路径。static代码块中主要是加载了当前目录下的库函数-HelloWorld,这里是为后续做铺垫,看不懂可以看到最后返回来在看就会茅塞顿开。

写完上述代码以后执行下边两个命令:

javac TestDib.java  # 编译java文件
javah TestDib 			#按照JNI的要求的规则生成C/C++的头文件(.h)

执行完成上边两行命令以后,会在你的目录下生成一个名为TestDib.h的文件,这个文件就是按照JNI要求的规则生成的,其实就是JNI对于生成函数的方法名做了限定,如果不按照这个规则生成就无法使用JNI调用。

我们看下生成的代码:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestDib */

#ifndef _Included_TestDib
#define _Included_TestDib
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TestDib
 * Method:    hello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_TestDib_hello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

当我们打开这个文件以后可能看到jni.h在报错,此时不要慌这是正常的。因为它找不到jni.h这个头文件,因为他在我们jdk的目录下放着,后续我们编译的时候需要去给他制定这个文件的位置。

接下来的操作需要有一点点的C/C++的代码知识,因为需要你创建一个TestDib.c的文件,这个文件中的要去实现我们在.h文件中生命的方法,如果映射到Java语言规范中理解为,头文件中的定义就是借口,你需要在.c的文件中去实现接口中的方法。代码如下,

#include <stdio.h>
#include <TestDib.h>
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_TestDib_hello(JNIEnv *env, jobject obj){
    printf("Hello, World!\n");
}

#ifdef __cplusplus
}
#endif

上述代码需要引入我们的头文件TestDib.h,就像我们Java中需要import一个包一样,引入的stdio.h是我们C的代码规范,因为下边我们需要用到printf函数,这个函数的声明位于stdio.h这个头文件中。entern "C"以及其他的代码都是从头文件中拿过来的就不做过多解释了,可以去百度查看。

写完.c文件以后我们需要对这个c的文件进行编译,执行如下指令,

g++ -dynamiclib  -I/$JAVA_HOME/include  -I/$JAVA_HOME/include/darwin   -o libHelloWorld.dylib  TestDib.c 

上边就是使用Gcc进行编译,两个-I就是在指定我们上边说的jni.h以及jni.h中包含的其他文件的位置,使用这个命令的前提是你的jdk的根目录需要配置在环境变量中。-o就是以什么文件名输出,文件名必须以lib开头,其他的系统倒是没有强制规定,但是mac的必须以lib开头,这里的名字还需要和我们一开始写的java代码相呼应,Java代码中利用 System.loadLibrary(“HelloWorld”);来加载名为HelloWorld的动态库,其实就是lib后边的名字。

执行完成上述命令以后,会在我们的目录下生成一个名为libHelloWorld.dylib的文件,至此我们的动态库链接生成好了。我们接下来可以使用java TestDib命令来执行我们的java代码,就可以看到控制台出如下:

image-20210801185746651

3.遇到的问题

在上述的操作中我主要遇到了一些关于M1芯片架构的问题,以及Jdk版本的问题。

1.mach-o but wrong architecture

? 这个问题主要是因为我的Gcc是使用的arm64的,但是实际我的jdk却是使用的x86的,所以在执行java代码的时候就会出现这个错误的架构的问题,之前一直知道,OpenJdk已经有arm架构了,但是没有换,这次终于碰到了,解决方案其实很简单,要么把Gcc调整成x86架构,要么把jdk调整为arm架构。

2.java.lang.UnsatisfiedLinkError:XXXXXXXXX()V

? 这个问题是我在我的windows电脑上遇到的,也码在这里吧,其实什么系统都可以遇到,一开始去翻网络上的各种答案,都说是你方法的大小写的问题,或者方法名没写对,让你仔细检查,但是无论如何我也没有检查出任何错误,都要崩溃了,突然想到在我的win使用过程中出现过jdk的位数问题,我就想是不是jdk版本的位数导致的,使用java -version果然jdk是32位,但是Gcc使用的是64位,这种位数的差异导致了方法一直找不到,所以通过提升jdk版本的位数就解决了这个问题,其实大多数人一般不太会遇到这个问题(尴尬)。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-02 10:40:39  更:2021-08-02 10:41:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年4日历 -2025/4/12 3:00:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码