欢迎点击「算法与编程之美」↑关注我们!java
本文首发于微信公众号:"算法与编程之美",欢迎关注。c++
互联网上已经有不少介绍 JNI 的入门教程,为何还要画蛇添足写本文呢?算法
相信你们在平时阅读一些教程类文章时都遇到过这样的状况,按照教程描述的步骤一步步的来操做,结果却并无获得教程指望的结果。遇到各类各样的问题,最后解决不了这些问题,进而放弃,放弃的缘由很简单那就是对于一个未知的领域,读者遇到问题后没法本身解决,而所阅读的教程类文章并无对这一问题进行详细的描述,致使最后选择了放弃。编程
经过分析咱们发现,大多的教程类文章都有一个共同的问题,就是重步骤而忽视读者在阅读过程当中可能遇到的问题的分析。windows
咱们指望改变这一现状,对于教程类文章,咱们不只介绍具体的操做步骤,并且更重要的是介绍读者在进行操做时可能遇到的一些关键问题,并对这些问题进行详细分析,从而帮助您完全的解决问题。微信
下面将介绍编写 JNI 入门教程HelloNative程序的编写。编程语言
主要的步骤为:函数
1) 编写 HelloNative.java 程序;工具
2) 编译并获得 HelloNative.h 头文件;学习
3) 编写 HelloNative.c 程序;
4) 编译动态连接库libHelloNative.jnilib;
5) 运行HelloNative程序。
先从总体上了解一下咱们须要作的事情有哪些,接下来我将介绍在mac 系统下每个步骤的详细内容并标注难点。
public class HelloNative{ static{ System.loadLibrary("HelloNative"); //难点一 } public static native void sayHello(); public static void main(String[]args){ new HelloNative().sayHello(); } }
执行以下命令:
javac HelloNative.java javah HelloNative
#include <stdio.h> #include "HelloNative.h" JNIEXPORT void JNICALL Java_HelloNative_sayHello(JNIEnv *env, jclass jc) //难点二 { printf("Hello Native\n"); }
//难点三
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/
java HelloNative
虽然上面介绍的是mac 系统下程序的编写,读者的系统可能大可能是windows,可是不影响你们的编写。
接下来将对第2节中标记的三个难点进行分析,这三个难点也是你们遇到的最多的问题。
JNI存在的意义就在于可以让 Java 程序和 C/C++等其余编程语言之间可以很是方便的交互,经过JNI 咱们就可以很是方便的作到这一点。
有些同窗可能会问为何要这样作?全部的任务我都用 Java 来作不是更好吗,为何一会用 Java 一会用C/C++,这样多语言岂不是更麻烦吗。
这个问题其实有多方面的缘由,如下将列举几点缘由:
- Java语言是运行在 JVM 之上的,所以对JVM 依赖的很是高。众所周知,这样的机制使得 Java 语言相对其余C 语言来讲效率变得低下,所以一些对执行效率要求较高的任务咱们能够用 C 语言来编写,而后上面的程序能够经过JNI 来调用 C 编写的模块。
- 假设你的项目组是一个多语言的组,存在着 Java、Ruby、Python 等多种编程语言的人员,如何让这些人员编程的程序可以相关调用呢?
那么 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
这个知识点很是有用,难点3中会再次涉及,请你们务必提早掌握。
这个函数的定义你们可能会写错,并且你们一般状况下不能彻底按照教程来写。
若是你们写过 c/c++ 语言的程序应该都知道,在c/c++中,一个程序分为头文件 hello.h 和其实现文件hello.c,其中在头文件中定义了函数声明,而在实现文件中对函数进行实现。
在了解这个知识点之后,咱们就知道 HelloNative.c 文件中应该如何突破难点2了。
打开 HelloNative.h 文件,找到对应的函数声明。
JNIEXPORT void JNICALL Java_HelloNative_sayHello (JNIEnv *, jclass);
函数声明你们应该都能看懂,本例中你们须要注意的是,在形参的声明中能够不指定形参的名称,而只是给出形参的类型。所以本例中的JNIEnv* 和jclass都是形参的类型。
因此难点二中你们看到的代码是下面这样,其中env和jc是具体的形参名称。
JNIEXPORTvoid JNICALL Java_HelloNative_sayHello(JNIEnv *env, jclass jc) //难点二
这个难点也是你们遇到的最多问题的地方,须要重点阐述。
如何编译一个C 语言的动态库?
你们都知道 C 语言一个很是著名的编译器叫gcc,所以本文介绍如何用 gcc 来编译动态库。(这里面你们须要注意的是 gcc 只是c语言编译器其中的一种,除了 gcc 还有不少其余的编译器如微软等公司出品的。)
不一样的操做系统下编译动态库的命令也是不同的,如:
Windows下: gcc -shared HelloNative.c -o HelloNative.dll Linux 下: gcc -shared HelloNative.c -o libHelloNative.so Mac 下: gcc -dynamiclib HelloNative.c -o libHelloNative.jnilib
在难点一中咱们给你们阐述了不一样的系统中动态库的名称是不同的,本节就有所体现。
其次,咱们还须要给出gcc编译动态库中jni.h和jni_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/
注意:
此处咱们只给出 mac 下的路径,其余系统相似。
本文介绍了 JNI 的入门教程HelloNative程序的编写,相对于其余的教程,咱们更加剧视你们在实际学习过程当中的一些难点,经过对三个难点的分析,相信你们可以更好的掌握相关的知识点。
如您对此种教程类文章感兴趣,欢迎持续关注“算法与编程之美”微信公众号,了解更多此类文章。