JNI教程(二):初探JNI

2 篇文章 0 订阅
1 篇文章 0 订阅

欢迎浏览我的博客 获得更多精彩文章
https://boyn.top

JNI教程(二):初探JNI

JNI开发流程

  • 编写Java程序,声明好要使用native的方法
  • 编译Java程序
  • 创建C/C++头文件
  • 编写C/C++程序
  • 创建链接库
  • 运行Java程序

下面将分步骤来演示一个简单的JNI开发过程

1.编写Java程序

本地方法在Java中是没有实现的方法体的,我们需要先声明这个方法,才能使用,native方法在声明结束后直接用一个分号结尾,下面是一个实例

编写一个Test.java

1 public class Test{
2     public native void hello();
3 }

编写一个Main.java

public class Main{
	public static void main(String[] args){
		Test test = new Test();
		test.hello();
	}
}

本地方法可以声明为public,private,protected, 也可以是static 或者非static 的,一个类文件中,可以有任意多个native方法

需要注意的是,你不能将native方法声明成abstract方法,同时,native关键字只能够用于方法,不能用于变量的声明和类的声明.

下面我们来写一个以后要用的到例子

package top.boyn;

public class HelloJNI{
	static{
		System.loadLibrary("beginningJni");
	}

	public native void hello();

	public static void main(String[] args){
		HelloJNI hello = new HelloJNI();
		hello.hello();
	}
}

HelloJNI.java表明了:

  • native方法是在运行时进行动态库链接的,而不是编译时,所以我们在写好后,可以将这个java文件编译成字节码,在运行时才要指定动态库

    加载动态链接库

    假设我们已经写好了一个动态链接库(beginningJava.dll),我们可以以这样的方式来将这个库加载到JVM中

    System.loadLibrary("beginningJava"); //第一种
    Runtime.getRuntime().loadLibrary("beginningJava"); //第二种
    

    注意,在加载这个库时,不需要写出链接库文件的后缀名,我们在Java程序中只需要给出文件名,在做一些跨平台程序时,直接替换dll文件,而不用更改java代码.

    那么,loadLibrary()方法是怎么知道去加载这些库的呢?我们可以用两种方法让JVM去知道这些链接库的位置

    • 将库文件放在系统变量: PATH(Windows平台)\LD_LIBRARY_PATH(UNIX平台)的路径下
    • 在运行JVM时,加参数指定加载库文件的路径 java -Djava.library.path="Your library path" your-class-name

2.编译java程序

编译声明了native方法的类跟编译普通的类流程一模一样,所以这一部分没有什么好说的,直接用javac来编译就好了

javac HelloJNI.java

3.创建C/C++头文件

在你开始用C/C++实现这些native方法之前,我们需要创造一个包含方法声明的头文件.幸好的是,java已经提供了一个生成头文件的工具给我们,名为javah.所以我们就用这个工具来生成头文件吧

在项目的主目录下,我们运行

javah top.boyn.HelloJNI

就可以看到在项目的主目录下出现了一个top_boyn_HelloJNI.h的头文件,命名的规则是将类前缀的点(.)改为下划线,将类名全部大写,你可以设定-o参数来指定输出的名字,不过还是推荐用默认的规则来生成头文件

如果运行的时候提示找不到类文件,我们就要用-cp参数指定我们项目的主目录

javah -cp YOUR_PROJECT_DIRECTORY top.boyn.HelloJNI

你不需要去关心这些头文件是如何组成的,唯一需要关心的就是,我们在java中声明的native方法在头文件中的样子

JNIEXPORT void JNICALL Java_top_boyn_HelloJNI_hello
  (JNIEnv *, jobject);

JNIEXPORT 和 JNICALL是两个宏,void表明这个方法不返回任何值.

在Java方法声明中,我们没有定义参数,但是在本地方法中有一个JNIEnv的指针和一个jobject变量,将这个当做一个规则就好

JNIEnv是一个指针,指向包含了Java环境和native环境之间的函数关系表的对象.第二个参数是jobject,如果我们把这个方法声明为static的话,那这个就是jclass,这个参数就和c++里面的this类似

4.编写C/C++程序

编写对应的C++程序 helloJNI.cpp

#include <stdio.h>
#include <jni.h>
#include "top_boyn_HelloJNI.h"

JNIEXPORT void JNICALL Java_top_boyn_HelloJNI_hello(JNIEnv *env, jobject obj){
    printf("Hello JNI\n");
    return;
}

在这个程序中,include了三个文件 stdio.h 是用作STDIO演示的, jni.h 用于引入跟JNI有关的函数实现,第三个头文件是我们在上一节中定义的.

为了要编译这个程序,我们要在编译的时候指定jni.h文件的位置,这个文件跟你安装的位置有关,一般要引入两个路径:

  • JDK_HOME\include
  • JDK_HOME\include\win32(或你的平台名字)

5.创建动态链接库

在这一节中,我们要将这个文件编译成动态链接库文件

关于如何安装c++编译器,网上已经有太多的教程了,在这里就不再赘述

以linux为例,我会演示如何将cpp文件编译成so文件

g++ -shared -I /usr/local/jdk1.8.0_202/include -I /usr/local/jdk1.8.0_202/include/linux -o beginningJava.so helloJNI.cpp

6.运行Java程序

我们在第二节中,已经编译好了Java的类文件,我们只需指定动态链接库的位置,就可以运行这个java程序了

java -Djava.library.path=/home/boyn/Code/Java/JNI/ top.boyn.HelloJNI
Hello JNI

特别要注意的是,如果我们是在linux下运行的话,.so文件要加上lib前缀,才能够被linux系统识别到

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值