文章目录

  • 背景
  • 介绍
  • 什么是JNI?
  • 什么是本地库?
  • 开发Java使用JNI本地库步骤
  • 编写Java类实现JNI本地调用
  • windows系统下编译动态链接库
  • 创建Java项目(demo)
  • 第一步:编写带有native的Java类
  • 第二步:javac生成`NativeDemo`类的字节码文件
  • 第三步:javah根据字节码文件生成jni头文件(NativeDemo.h)
  • 创建C++动态链接库(DLL)
  • 第一步:打开Microsoft Visual Studio 2022创建项目
  • 第二步:配置项目并创建
  • 第三步:加载javah生成的NativeDemo.h头文件
  • 第四步:配置jni.h导入找不到问题
  • 第五步:编辑源码,导入`NativeDemo.h`头,实现对应方法。
  • 第六步:执行本地windows调试器,生成.dll动态库文件
  • 创建Java程序入口类,加载动态库并运行
  • 第一步:创建Main.java文件,加载动态库,调用方法
  • 第二步:编译Main.java、运行Main
  • Linux系统下编译动态链接库
  • 附录
  • javac参数help
  • 出现的一些问题
  • 关于为什么在`javac`编译的时候要指定`-encoding`选项?
  • 关于为什么要把NaviveDemo.h头文件复制到根目录后加载?
  • 参考链接


背景

        最近学习了Java中调用本地程序(JNI:Java Native Interface)、多种语言混合编程。但是关于Java怎么使用JNI调用本地程序是一点儿都不清楚,网上查阅了很多资料,编写了一个demo做简单调用机制的了解。

        jdk自身在开发中也会调用自己的动态库(windows下的.dll文件),如下图:

linux找不到java环境变量报错_linux找不到java环境变量报错

介绍

什么是JNI?

    JNI全称叫Java Navtie Interface,中文翻译本地调用。

C/C++是系统级的编程语言, 可以用来开发任何和系统相关的程序和类库, 但是Java本身编写底层的应用比较难实现, 使用JNI可以调用现有的本地库, 极大地灵活了Java的开发.
C/C++的效率是目前最好的语言, 可以使用C/C++来实现一些实时性非常高的部分. C/C++和Java本身都是非常流行的编程语言, 一些大型软件中经常使用语言之间的混合编程.
以上转载自:(在Java中调用C/C++本地库)

什么是本地库?

本地库被分为静态库(.a和.lib结尾)动态库(.dll和.so结尾)

  1. 在windows系统下动态库以.dll结尾
  2. 在linux系统下动态库以.so结尾

开发Java使用JNI本地库步骤

  1. 编写Java类,方法要带有native关键字
  2. javac编译Java类,生成class文件
  3. javah生成native对应的头文件(头文件.h)
  4. C/C++实现javah生成的头文件,编译生成.dll动态库
  5. 动态库复制到java类的运行目录下,加载本地库后调用方法、运行

编写Java类实现JNI本地调用

windows系统下编译动态链接库

  • 开发环境:
  1. windows 11系统
  2. jdk8
  3. Visual Studio Code IDE,编写Java代码
  4. Microsoft Visual Studio Community 2022 (64 位),编写C++代码,生成dll动态库
  • 项目结构:

linux找不到java环境变量报错_linux找不到java环境变量报错_02

JNI_TEST
  --java-src   # java源码文件夹 & .dll动态库
  --jni_dll    # c++源码文件夹 & 生成的目标文件(.dll)

创建Java项目(demo)

第一步:编写带有native的Java类
/**
 * Java 本地native方法,具体由C++实现
 */
public class NativeDemo {
    /**
     * 由C++返回一个字符串,通过Java打印出来
     */
    public native String returnString();

}
第二步:javac生成NativeDemo类的字节码文件

打开命令提示符窗口,编译NativeDemo.java文件,生成字节码文件。

linux找不到java环境变量报错_动态链接库_03

第三步:javah根据字节码文件生成jni头文件(NativeDemo.h)

linux找不到java环境变量报错_动态链接库_04


        生成的NativeDemo.h,就需要#include 'NativeDemo.h'导入,通过C++去实现了。

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

#ifndef _Included_NativeDemo
#define _Included_NativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     NativeDemo
 * Method:    returnString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_NativeDemo_returnString
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

创建C++动态链接库(DLL)

第一步:打开Microsoft Visual Studio 2022创建项目

搜索dll,创建动态链接库。

linux找不到java环境变量报错_java jni_05

第二步:配置项目并创建

linux找不到java环境变量报错_java jni_06

第三步:加载javah生成的NativeDemo.h头文件

        把javah生成的NativeDemo.h头文件复制到jni_dll文件夹的根目录下。加载生成的头文件。

linux找不到java环境变量报错_Java_07


linux找不到java环境变量报错_java jni_08

第四步:配置jni.h导入找不到问题

        打开加载的NativeDemo.h头文件后,发现jni.hJNIEXPORTJNICALL加载不到,需要对项目进行配置,导入jdk相关到项目,才能使用jni.h。

linux找不到java环境变量报错_Java_09


linux找不到java环境变量报错_java jni_10


选择完成,应用后,导入的jni.h头文件不会报错了。

第五步:编辑源码,导入NativeDemo.h头,实现对应方法。

打开pch.cpp源码文件,编辑。

linux找不到java环境变量报错_Java_11

// pch.cpp: 与预编译标头对应的源文件

#include "NativeDemo.h"
#include "pch.h"
#include <iostream>

// 当使用预编译的头时,需要使用此源文件,编译才能成功。
/*
 * Class:     NativeDemo
 * Method:    returnString
 * Signature: ()Ljava/lang/String;
 * 复制NativeDemo.h头中定义的方法,c++去实现
 */
JNIEXPORT jstring JNICALL Java_NativeDemo_returnString
(JNIEnv* env, jobject jobj) {

	return env -> NewStringUTF("exec dll: this is return from c++");
}
第六步:执行本地windows调试器,生成.dll动态库文件

linux找不到java环境变量报错_动态链接库_12


        在jni_dll项目的根路径下,x64/Debug/jni_dll.dll就是生成的目标文件,后续被调用的本地动态库也是这个文件。

        到此,创建并生成.dll动态链接库已经完成。

创建Java程序入口类,加载动态库并运行

第一步:创建Main.java文件,加载动态库,调用方法
/**
 * 程序运行入口
 */
public class Main {

    static {
        /**
         * 加载动态库:只需要加载名字就可以,不需要带上.dll后缀,动态库放在和Main运行文件同一个目录下.
         * window上的动态库以.dll结尾,linux动态库以.so结尾
         */
        System.loadLibrary("jni_dll");
    }

    public static void main(String[] args) {
        // 创建Java对象,直接调用方法,具体的实现交给C++底层处理
        NativeDemo demo = new NativeDemo();
        // 调用方法, 由动态库制定并返回
        System.out.println(demo.returnString());

    }
}
第二步:编译Main.java、运行Main

        复制刚生成的jni_dll.dll动态库到可运行文件所在目录,动态库会在此目录下加载。

linux找不到java环境变量报错_linux找不到java环境变量报错_13

Linux系统下编译动态链接库

待补充…

附录

javac参数help

linux找不到java环境变量报错_java jni_14

出现的一些问题

关于为什么在javac编译的时候要指定-encoding选项?

        大多数ide默认的编码环境是utf-8,windows命令提示符窗口,默认是GBK编码。如果使用javac命令直接编译,会出现字符不兼容的错误。所以在javac编译的时候指定使用utf-8的方式进行。

linux找不到java环境变量报错_动态链接库_15


        可以使用chcp命令查看当前命令提示符窗口的编码方式。

linux找不到java环境变量报错_Java_16


        修改编码方式后编译同样失败,可能的原因是当前窗口的编码对编译并不生效。

        查阅了相关资料,如果直接对语言和区域的格式进行调整,可能会造成在dos窗口下导致其他字符显示错误。

linux找不到java环境变量报错_java jni_17

关于为什么要把NaviveDemo.h头文件复制到根目录后加载?

        在Microsoft Visual Studio 2022版本编辑器下,加载的头文件不会直接导入项目,在使用#include 'NativeDemo.h'导入头文件的时候,还是会出现找不到头文件的情况。