Java JNI简单流程(Win10)

JNI 介绍

来自于百度百科

​JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。 [1]  从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。

简单来说JNI就是解决Java与C或C++之间的互相调用的问题。

前期准备:

1. Java 环境(Java 基础环境)

2. GCC 环境(编译共享库需要用到,建议官网下载压缩包,online版本可能在安装的时候报错下载地址

开始:

1. 创建一个HelloJni.java 类,里面定义一个native 方法,表示该方法使用C或C++实现

HelloJni.jav
public class HelloJni {
    // 定义native 方法。
    private native void sayHello();
}

2. 通过Java 环境自带方法生成 .h 头文件。在Jdk10的时候为了简化Jdk的方法,将javah命令放在了javac里面,所以不同的jdk有两种生成 .h 头文件:

JDK10 版本以下:

javah -classpath . -jni HelloJni.java 

JDK10 版本及以上:

 javac -h . HelloJni.java  会在当前目录下生成一个HelloJni.h 文件

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

#ifndef _Included_HelloJni
#define _Included_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloJni
 * Method:    sayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloJni_sayHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

3. 创建一个Hello.cpp文件,导入这个头文件并且实现Java_HelloJni_sayHello 方法:

Hello.cpp
#include "HelloJni.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_HelloJni_sayHello
  (JNIEnv *, jobject){
      printf("Hello JNI");
}

4.  gcc 编译dll库:

gcc -Wl,--add-stdcall-alias -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o hello.dll Hello.cpp

在编译的时候需要将jni.h 和jni_md.h 文件拷贝到与cpp 文件相同目录下:

JDK目录的include目录下有一个jni.h的文件,include的win32目录下有个jni_md.h文件

共享库在Win版本上是.dll, 在linux上是.so的后缀。

5. 将编译的.dll文件加载到HelloJni.java文件里面使用:

HelloJni.java
public class HelloJni {
    static {
        // 加载共享库
        System.loadLibrary("hello");
    }
    public static void main(String[] args) {
        new HelloJni().sayHello();
    }

    // 定义native 方法。
    private native void sayHello();
}

不同的系统,使用System.loadLibrary方法会加载不同的hello文件,比如在Win系统会自动加载hello.dll文件,linux系统会自动加载hello.so文件。

6. 使用javac 命令将.java 方法编译为可运行文件:

javac HelloJni.java

7. 使用java 方法执行class 文件:

java -Djava.library.path=./ HelloJni

-Djava.library.path=./ 表示使用当前目录下的共享库,如过共享库没有在当前目录下并且不添加这个参数会显示找不到共享库而编译失败。

整个过程遇到的问题点和解决方法:

1. 找不到编译好的共享库

D:\Codes\Java\JINProj\src\com\xlf\test>E:\JavaTools\JDK11\bin\java -Djava.library.path=. HelloJni
Exception in thread "main" java.lang.UnsatisfiedLinkError: HelloJni.sayHello()V
        at HelloJni.sayHello(Native Method)
        at HelloJni.main(HelloJni.java:7)

产生的原因:

最开始我给HelloJni.java设置了一个package,后面因为重新创建了一个文件来存放这个类,所以删除了包名。之后没有重新生成.h 文件,导致.h里面的方法名不对,编程了Java_com_xlf_test_HelloJni_sayHello。最后调用native方法的时候由于命名不匹配导致找不到这个方法。

解决办法:

重新生成一个.h文件,并且重新生成.dll文件。

2. java 执行class文件失败

D:\Codes\Java\JINProj\src\com\xlf\test>java -Djava.library.path=./ HelloJni
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.UnsupportedClassVersionError: HelloJni has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

产生原因:

使用javac版本与java版本不匹配。

我本地最开始安装的是jdk8,之后又下载了jdk11,并且将jdk11添加到了环境变量中,导致java版本为jdk8,javac版本为jdk11的。

查看版本可以使用--version 方法查看,比如:

java --version

javac --version

解决方法:

使用相同的版本编译和执行,可以使用绝对路径的方法执行命令:

比如:E:\JavaTools\JDK11\bin\javac --version

E:\JavaTools\JDK11\bin\java --version

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值