JNI 原理与入门
来源:互联网 发布:魔术师约翰逊生涯数据 编辑:程序博客网 时间:2024/06/06 13:22
- JNI 是什么
- HelloWorld
- 编写 java 文件
- 编译 java 文件
- 编写 c 语言文件
- 编译 c 语言文件
- 运行
- Android 中的 JNI 简介
- NDK
- Native 层和 JNI 层
- 附录
- 附录1让 Java 自动帮我们生成C函数名
- 附录2如何生成 dll
- 方法1 使用 VisualStudio
- 方法2 使用 cl 命令行
JNI 是什么
- JNI = Java Native Interface
- 是java调用C语言用的
- 是java调用 “用C语言代码编译出来的库” 用的
- 因为用C语言编写的程序是不可移植的,是平台相关的,所以这种代码一般叫做Native Code
- 举例来说,用C语言在Windows上写了一个HelloWorld,用VC编译成了dll,这个dll拿到Linux上是运行不起来的。
- 这就是为什么叫做 “Native” Interface了
HelloWorld
- 我们先搞个 JNI 的 helloworld,来个感性的认识。
- 本例实现的是一个 java 的 helloworld 程序,但其中的打印 “HelloWorld!” 的功能,是用 C 语言实现的。
- 本例基于 Windows7、JDK1.6,用记事本和cmd命令行完成,下面是详细步骤。
1. 编写 java 文件
- 用记事本写一个最简单的 HelloWorld.java
- 其中打印“HelloWorld”那句话交给C语言去完成,而不再
System.out.println()
public class HelloWorld { public native void displayHelloWorld(); static { System.loadLibrary("hello"); } public static void main(String[] args) { new HelloWorld().displayHelloWorld(); }}
- 1:
public native void displayHelloWorld();
这句话把displayHelloWorld()
方法声明为 native 的,也就是说这个方法由C语言的库来提供,我们不要去实现它。那么C语言的库在哪里呢? - 2:
System.loadLibrary("hello");
这句话的意思就是加载C语言的 hello 这个库,这个库在Windows上是一个叫 hello.dll 的文件,一会儿我们会写相应的C代码去实现displayHelloWorld()
,并编译成dll文件供java使用。 - 3:
System.loadLibrary("hello");
这句话一般写在 static 块里,static{}
不是这篇的重点,不解释
2. 编译 java 文件
- 我们先把这个 HelloWorld.java 编译成class字节码:
javac HelloWorld.java
3. 编写 c 语言文件
- 下面我们用C语言来实现
displayHelloWorld()
- 记事本新建一个文件叫 HelloWorldC.c,内容如下:
#include "jni.h"JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj){ printf("Helloworld!\n"); return;}
- 1:include 必须要有,jni.h 是 java sdk 提供给我们的,具体路径在:
%java_home%\include
- 2:这个函数名必须是前缀
Java_包名_类名_C函数名
,参数必须是(JNIEnv* env,jobject obj,其他参数,..)
。 - 3:函数名和参数太长记不住没关系,我们后面会讲如何借助工具自动生成。
4. 编译 c 语言文件
- 写好c文件之后,我们编译它,生成 dll。
- 如何把c文件编译生成dll不是本文的重点,我们必须得到 hello.dll 就对了。关于如何编译,本文附录部分会提供两种方法。
- 注意,生成的dll文件名必须叫 hello.dll,因为我们的java代码里 load 的就是 “hello”(
System.loadLibrary("hello");
)
5. 运行
- 好了,保证 hello.dll 和 HelloWorld.class 在同一目录下,我们运行:
java HelloWorld
- 没问题的话,就可以看到打印出 HelloWorld 了
- 这个例子说明 java 确实可以使用 c 实现的功能。
Android 中的 JNI 简介
- Android 系统的底层是 Linux,Linux 是由大量的 c 语言库构建起来的
- 而 Android 的应用程序却都是 Java 开发的。所以 Android 系统中大量的使用了 JNI。
- 我们上面的 HelloWorld, 是运行在 Windows 的 java 程序去调用 Windows 上的 c 库函数。
- 而在 Android 上,一般是 App(即运行在 Android 上的 java 程序) 去调用 Android 上的 c 库函数。
- 也就是说,如果我们上面的 HelloWorld 如果想在 Android 上做,则:
- HelloWorld.java 要变成 HelloWrold.apk。HelloWrold.apk 起码要有一个 Activity,上面起码要有个 TextView,来显示 “HelloWorld!”
- hello.dll 要变成 hello.so。不再是用 VC 生成 dll,而是要用 gcc 或者什么东西,生成一个可以在 Android 上运行的 so 库。
- 其中第一个问题好办,我们用 eclipse 或 AndroidStudio 可以轻松搞一个 apk。
- 第二个问题需要把 c 语言代码编译成能在 Android 上运行的库文件,这需要 Google 的专门工具:NDK。
NDK
- NDK 是 Native Development Kit 的缩写,是谷歌提供的 C/C++ 编译工具链,专为 Android 准备。
- NDK 本质上是编译器、连接器的集合。编译器是 gcc/clang,连接器是 ld,等等。
- 简单来说,NDK 就是一个 toolchain,能把 c/c++ 源码编译成运行在 Android 上的库文件/可执行文件。
- 他的使用方式跟 gcc 很像:书写 Makefile 来描述你项目的组织结构,使用 make 命令来调用 gcc 工具链,实现编译目标。
- NDK的安装:只需要下载解压即可,解压之后可以拷贝到任意目录。下载地址:http://developer.android.com/tools/sdk/ndk/index.html
- 使用 NDK 的目的就是把 c/c++ 的源文件编译成库,供 App 调用。
- 使用 NDK 大体上有两种方式:
- 集成到IDE里,比如 eclipse 或者 AndroidStudio
- 直接使用命令行
- 无论哪种方式,其本质都是调用 NDK 的命令行:ndk-build,让 ndk 根据你书写的 Android.mk 去把你的 c 代码编译成库。
- 使用 NDK 的关键是编写 makefile。即通过 makefile 告诉 NDK,该把哪些源文件编译成哪个库。
- NDK 使用的 makefile 是经过谷歌改良的,跟正常的 makefile 语法稍有不同。一般叫做 Android.mk。
- 具体用法本文不展开。
Native 层和 JNI 层
- 我们现在知道了 Java 层和 JNI 层,一个是 Java 写的,一个是 C/C++ 写的。
- 其实一般用 C/C++ 写的程序,还分为两层:Native 层和 JIN 层。
- 为什么有这个区分呢?
- 所谓 Native 层,是不需要知道有 Java 层存在的,它只负责干活就行了。比如一些已有的 c 语言库,它的开发者根本不知道将来需要被 Java 层使用的。
- 而 JNI 层是知道 Java 层存在的,它往往不真的干活,而只是把 Java 的调用转交给真正干活的 Native 层去处理。
附录
附录1:让 Java 自动帮我们生成C函数名
- Java提供了一个工具:javah.exe,能根据你的 Java 代码,自动帮你生成需要的C函数的函数名,并保存成一个.h文件。
- 具体用法:
javah 包名.类名
- 注意:jdk 1.6 javah 需要的参数是.class文件,jdk 1.7 需要的是.java文件。
- 也就是说,如果你用的是 jdk1.6,则需要先把 java 编译成字节码才能运行 javah。
- 在我们上面的例子里(jdk1.6),编译好 HelloWorld.class后,我们运行:
javah HelloWorld
注意:保证执行 javah 命令的目录中存在 HelloWorld.class。注意不要写成
javah HelloWorld.class
执行完 javah 命令之后,会生成一个 HelloWorld.h 文件,其内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class HelloWorld */#ifndef _Included_HelloWorld#define _Included_HelloWorld#ifdef __cplusplusextern "C" {#endif/* * Class: HelloWorld * Method: displayHelloWorld * Signature: ()V */JNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
- 可以看到,他自动生成了函数名及参数,我们可以直接从里面复制。当工程量大了以后,生成这个头文件给C语言程序员去用是必要的。
附录2:如何生成 dll
方法1. 使用 VisualStudio
- 使用的是 VS2010,其他版本应该大同小异
新建项目时,要选择DLL项目而不是控制台应用程序项目。对VS2010来说,具体就是:
选“Win32项目”,而不是“Win32控制台应用程序”。Win32项目点确定 –> 下一步可以选DLL。把 jin.h 和 jni_md.h 让项目能引用到。
- 要么就去 jdk 的 include 下把他俩复制到你项目里,
- 要么就在项目上右键–>属性,C/C++—>常规—>附加包含目录,填写
<your_jdk_path>/include
和<your_jdk_path>/include/win32
VS默认生成的都是cpp文件,按c++编译。这样编译出来的 DLL 不能直接被 java 使用。有以下解决方法:
- 要么所有源文件后缀名改成.c,重新生成项目。
要么在头文件里加上如下内容(就是从 javah 生成的那个头文件里复制出来的):
#ifdef __cplusplus extern "C" {#endifJNIEXPORT void JNICALL Java_HelloWorld_displayHelloWorld (JNIEnv *, jobject);#ifdef __cplusplus }#endif
或者你直接 include javah 生成的那个头文件,就不用去解决cpp不兼容的问题了。
方法2. 使用 cl 命令行
- cl.exe 是 vc6.0 的编译器
- 网上可以下载到绿色版,叫 mycl.rar,挺小的,才1.53M
- 解压后直接使用其中的 cl.exe 即可
- 使用之前需要先设置如下环境变量:
CL_HOME=D:\ProgramFiles\mycl把 CL_HOME 加到 path 里去INCLUDE=%CL_HOME%\includeLIB=%CL_HOME%\lib
cl 的帮助可以用
cl /?
调出来编译以上 c 语言文件的完整命令行如下:
cl -I "%java_home%\include" -I "%java_home%\include\win32" -LD HelloWorldC.c -Fehello.dll
- 1:其中 -I 表示 include 的搜索路径 ,我们必须把”%java_home%\include”和%java_home%\include\win32”加上
- 2:-LD 表示需要生成dll
3:-Fe表示输出文件,后面紧跟文件名
另外,如果编译时提示LIBCMT.LIB找不到,可下载个VC6.0的安装包,在…./VC98/LIB/ 下可以找打这个文件,把它复制到mycl的LIB目录下即可了。
0 0
- JNI 原理与入门
- JNI原理与使用
- Android JNI开发入门与提高
- JNI原理
- JNI入门
- JNI入门
- JNI入门
- JNI入门
- JNI 入门
- JNI入门
- JNI入门
- JNI入门
- JNI入门
- jni入门
- JNI入门
- JNI入门
- JNI入门
- JNI入门
- 337. House Robber III
- HDU
- Uboot移植(smart210)
- 杭电 oj 3350 #define is unsafe
- bzoj1486: [HNOI2009]最小圈
- JNI 原理与入门
- Python获取当前时间及格式化
- 随手记录--线程
- Windows安装Pillow错误解决
- 漫谈C++:良好的编程习惯与编程要点
- 面向对象
- js时间戳、毫秒格式化
- Difference between Stack.capacity() and Stack.size()
- 数据结构—单链表