史上最详细的JNI入门教程HelloNative

简介: 史上最详细的JNI入门教程HelloNative

 

1 为什么写本文

互联网上已经有很多介绍 JNI 的入门教程,为什么还要多此一举写本文呢?

 

相信大家在平时阅读一些教程类文章时都遇到过这样的情况,按照教程描述的步骤一步步的来操作,结果却并没有得到教程期望的结果。遇到各种各样的问题,最后解决不了这些问题,进而放弃,放弃的原因很简单那就是对于一个未知的领域,读者遇到问题后无法自己解决,而所阅读的教程类文章并没有对这一问题进行详细的描述,导致最后选择了放弃。

 

通过分析我们发现,大多的教程类文章都有一个共同的问题,就是重步骤而忽视读者在阅读过程中可能遇到的问题的分析。

 

我们期望改变这一现状,对于教程类文章,我们不仅介绍具体的操作步骤,而且更重要的是介绍读者在进行操作时可能遇到的一些关键问题,并对这些问题进行详细分析,从而帮助您彻底的解决问题。

 

2 HelloNative教程

下面将介绍编写 JNI 入门教程HelloNative程序的编写。

 

主要的步骤为:

1) 编写 HelloNative.java 程序;

2) 编译并得到 HelloNative.h 头文件;

3) 编写 HelloNative.c 程序;

4) 编译动态链接库libHelloNative.jnilib;

5) 运行HelloNative程序。

 

先从整体上了解一下我们需要做的事情有哪些,接下来我将介绍在mac 系统下每一个步骤的详细内容并标注难点。

 

2.1 编写 HelloNative.java 程序

 

publicclass HelloNative{

static{

System.loadLibrary("HelloNative");  //点一

}

 

public static native voidsayHello();

 

public static void main(String[]args){

new HelloNative().sayHello();

}

}

 

2.2 编译并得到 HelloNative.h 头文件

 

执行如下命令:

javacHelloNative.java

javahHelloNative

 

 

2.3 编写 HelloNative.c 程序

 

#include<stdio.h>

#include"HelloNative.h"

 

JNIEXPORTvoid JNICALL Java_HelloNative_sayHello(JNIEnv *env, jclass jc) //点二

{

printf("Hello Native\n");

}

 

 

2.4 编译动态链接库libHelloNative.jnilib

 

//点三

gcc HelloNative.c

-o libHelloNative.jnilib

-dynamiclib

-I/Library/Java/JavaVirtualMachines

/jdk1.8.0_131.jdk/Contents/Home/include/

-I/Library/Java/JavaVirtualMachines

/jdk1.8.0_131.jdk/Contents/Home/include/darwin/

 

2. 5运行HelloNative程序

 

java HelloNative

 

 

3 难点分析

虽然上面介绍的是mac 系统下程序的编写,读者的系统可能大多是windows,但是不影响大家的编写。

 

接下来将对第2节中标记的三个难点进行分析,这三个难点也是大家遇到的最多的问题。

 

1) 难点一

JNI存在的意义就在于能够让 Java 程序和 C/C++等其他编程语言之间能够非常方便的交互,通过JNI 我们就能够非常方便的做到这一点。

 

有些同学可能会问为什么要这样做?所有的任务我都用 Java 来做不是更好吗,为什么一会用 Java 一会用C/C++,这样多语言岂不是更麻烦吗。

 

这个问题其实有多方面的原因,以下将列举几点原因:

- Java语言是运行在 JVM 之上的,因此对JVM 依赖的非常高。众所周知,这样的机制使得 Java 语言相对其他C 语言来说效率变得低下,因此一些对执行效率要求较高的任务我们可以用 C 语言来编写,然后上面的程序可以通过JNI 来调用 C 编写的模块。

 

- 假设你的项目组是一个多语言的组,存在着 JavaRubyPython 等多种编程语言的人员,如何让这些人员编程的程序能够相关调用呢?

 

那么 Java 是如何做到这一点的呢?

将其他语言编写的模块编译成动态库,然后在Java程序中加载这个动态库,进而使用该库。

 

System.loadLibrary("HelloNative");  //点一

因此大家看到的这行代码就是 Java 程序加载编译后的动态库HelloNative。这里面大家需要注意的是HelloNative 并不是最终动态库的全称,不同的操作系统下这个动态库的名称是不一样的,如:

Windows 下叫*.dll

Linux 下叫*.so

Mac 下叫*.jnilib

 

大家都知道 Java 是跨平台的程序,因此在Java 代码里面肯定不能指定某一种平台具体的动态库完整名称,因此只是给出了这个动态库的名称而非全称。

 

对于本例我们最终在不同平台下生成的动态库全称是:

Windows         HelloNative.dll

Linux               libHelloNative.so

Mac                 libHelloNative.jnilib

 

这个知识点非常有用,难点中会再次涉及,请大家务必提前掌握。

 

2) 难点二

 

这个函数的定义大家可能会写错,而且大家通常情况下不能完全按照教程来写。

 

如果大家写过 c/c++ 语言的程序应该都知道,在c/c++中,一个程序分为头文件 hello.h 和其实现文件hello.c,其中在头文件中定义了函数声明,而在实现文件中对函数进行实现。

 

在了解这个知识点以后,我们就知道 HelloNative.c 文件中应该如何突破难点2了。

 

打开 HelloNative.h 文件,找到对应的函数声明。

JNIEXPORT void JNICALL Java_HelloNative_sayHello  (JNIEnv *, jclass);

 

函数声明大家应该都能看懂,本例中大家需要注意的是,在形参的声明中可以不指定形参的名称,而只是给出形参的类型。因此本例中的JNIEnv* jclass都是形参的类型。

 

所以难点2中大家看到的代码是下面这样,其中envjc是具体的形参名称。

JNIEXPORTvoid JNICALL Java_HelloNative_sayHello(JNIEnv *env, jclass jc) //点二

 

3) 难点三

这个难点也是大家遇到的最多问题的地方,需要重点阐述。

 

如何编译一个C 语言的动态库?

 

大家都知道 C 语言一个非常著名的编译器叫gcc,因此本文介绍如何用 gcc 来编译动态库。(这里面大家需要注意的是 gcc 只是c语言编译器其中的一种,除了 gcc 还有很多其他的编译器如微软等公司出品的。)

 

不同的操作系统下编译动态库的命令也是不一样的,如:

Windows下:

gcc -shared HelloNative.c -oHelloNative.dll

 

Linux 下:

gcc -shared HelloNative.c -olibHelloNative.so

 

Mac 下:

gcc -dynamiclib HelloNative.c-o libHelloNative.jnilib

 

在难点1中我们给大家阐述了不同的系统中动态库的名称是不一样的,本节就有所体现。

 

其次,我们还需要给出gcc编译动态库中jni.hjni_md.h两个头文件的路径。

 

因此,我们接下来要找到这两个头文件所在的目录,你可以通过各种各样的文件搜索工具( windows 下的强大的搜索神器everything)找到他们。在 Linux Mac 下面我们可以通过下面的命令快速找到:

locate jni.h

>/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/jni.h

 

locate jni_md.h

>/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin/jni_md.h

 

注意,我们只需要两个文件所在的目录:

/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/

/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/include/darwin/

 

准备工作做好了,最后我们加上这个头文件的路径就大功告成了。

Mac 下:

gcc -dynamiclib HelloNative.c-o libHelloNative.jnilib

-I/Library/Java/JavaVirtualMachines

/jdk1.8.0_131.jdk/Contents/Home/include/

-I/Library/Java/JavaVirtualMachines

/jdk1.8.0_131.jdk/Contents/Home/include/darwin/

 

注意:

-I中的I为大写;

-I/Library中的I/Library之间没有空格。

 

此处我们只给出 mac 下的路径,其他系统类似。

 

4 总结

本文介绍了 JNI 的入门教程HelloNative程序的编写,相对于其他的教程,我们更加重视大家在实际学习过程中的一些难点,通过对三个难点的分析,相信大家能够更好的掌握相关的知识点。

目录
相关文章
|
8月前
|
存储 算法 编译器
程序与技术分享:C++模板元编程简介
程序与技术分享:C++模板元编程简介
66 0
|
存储 自然语言处理 Linux
0基础C语言自学教程——收官之战——第十四节 文件的编译和链接
这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
172 0
0基础C语言自学教程——收官之战——第十四节 文件的编译和链接
|
存储 缓存 安全
NDK 系列(5):JNI 从入门到实践,万字爆肝详解!(下)
NDK 系列(5):JNI 从入门到实践,万字爆肝详解!(下)
293 0
NDK 系列(5):JNI 从入门到实践,万字爆肝详解!(下)
|
算法 Java Linux
NDK 系列(5):JNI 从入门到实践,万字爆肝详解!(上)
NDK 系列(5):JNI 从入门到实践,万字爆肝详解!
642 0
NDK 系列(5):JNI 从入门到实践,万字爆肝详解!(上)
|
存储 安全 Java
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(15)
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
112 0
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(15)
|
存储 Java 程序员
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(7)
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
108 0
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(7)
|
Java
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(4)
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
106 0
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(4)
|
消息中间件 算法 NoSQL
JVM史上最佳入门指南
提到Java虚拟机(JVM),可能大部分人的第一印象是“难”,但当让我们真正走入“JVM世界”的时候,会发现其实问题并不像我们想象中的那么复杂。唯一真正令我们恐惧的,其实是恐惧本身。而作为整个JVM系列的首篇,本文将带你解除刚开始学习JVM时的种种疑惑。比如:什么是JVM?为什么学习JVM?怎么有效的学习JVM?带着以上的这些问题,让我们一起走入JVM的世界吧。
167 0
|
存储 Java 程序员
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏(1)
怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏
147 0
|
运维 算法 架构师
JAVA技术周刊第一期:关于JVM你了解多少?看这篇文章就够了!
Java技术周刊正式上线,最新的Java技术与动态、预告活动、最热问答、直播教程等沉淀,订阅“JAVA开发者”技术圈获取更多干货内容!
29006 0
JAVA技术周刊第一期:关于JVM你了解多少?看这篇文章就够了!