直到,面临毕业,稀里糊涂地投了一份应聘Android软件开发的岗位。当接到面试官的电话,问到“JNI是什么?”的时候,支支吾吾三言四语也说不出个你死我活。最后,面试的结果也是石沉大海,杳无音讯。从此,对JNI的讨厌之情,入骨三分,同时,又多了份感悟:JNI是个令人头痛,却又不得不学的东东。
就这样,找资料,看文档,一步步来,翻过了几座障碍,打通了Java到C的道路,那种喜悦,尽然让我哑然失笑。。。从此,觉得JNI不再那么邪恶。。。继续前行,,,后来发现,仅仅打通了道路,仅仅只能走个字符串,走个指令,想实现复杂的功能时,才知道,了解JNI的原理和规范刻不容缓,于是乎,Download一份英文JNI档案,不分昼夜地研磨,并在这里留下学习的脚印。。。
闲话少说,步入正文。
我们知道,Java应用程序是一处编码,处处运行的,之所以可以这么威风,靠的就是JVM这个东西,那什么是JVM呢,JVM乃Java虚拟机,是一种虚拟技术,位于java应用程序和特定的操作系统之间,担当着“一处编码,处处运行”的重任。他隐藏了操作系统的差异性,使得运行在上层的java程序可以不用管底层到底是神马操作系统。这样,只要你的机子上有JVM,那么你的机子就可以跑java程序,至于你用的是神马操作系统,我不用管。
可是,有滴时候,我们并不能抛开特定的操作系统的特性,而是用纯粹的java来完成我们的应用。虽然,java是多么多么滴强大,但是他也不是万能的。比如,现在我们的应用需要一些特定操作系统的特性,但是java它不支持,怎么办?或者,我们需要的部分核心功能已经存在了,但是是C/C++的库文件,更常见的一个理由是“这个处理过程性能要求高,java性能忒差,满足不了客户要求,用C/C++吧,性能高”!
如此可见,JNI的存在,总有它的理由。但是JNI的使用,我们破坏了java“一处编码,处处运行”的优势,使得我们的应用是“Host-Dependable”。同时,Java是类型安全的,而C/C++不是滴,所以,使用JNI,java这两大特性就荡然无存了。也因此,要不要使用JNI,我们需要三思而后行。。。
扯了这么远,回过头来,JNI是如何使得Java和C/C++想通的?我们知道,Java之所以不能和C/C++想通,最主要的原因就是类型差异。
关于JNI如何处理类型差异的,后面再说。这里,我们看看Java的数据类型String和C语言的Char*类型是在可见的(黑盒)层面如何相互沟通的。
我们使用Java写个类Jni.java:
- package demo.jni;
- public class Jni {
- public native int add();
- public native String getString();
- }
在使用javac编译该文件生成Jni.class文件后,我们使用javah -jni来生成一个C语言的头文件(关于javah命令的正确使用,请看我的另一个帖子),该文件名称是demo_jni_Jni.h:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class demo_jni_Jni */
- #ifndef _Included_demo_jni_Jni
- #define _Included_demo_jni_Jni
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: demo_jni_Jni
- * Method: add
- * Signature: ()I
- */
- JNIEXPORT jint JNICALL Java_demo_jni_Jni_add
- (JNIEnv *, jobject);
- /*
- * Class: demo_jni_Jni
- * Method: getString
- * Signature: ()Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_demo_jni_Jni_getString
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
可以看到,生成的本地方法中,第一个参数类型是JNIEnv*,是一个指向JNIEnv的指针类型,那么JNIEnv是一个什么类型呢?
JNIEnv是一个指向了一个Pointer,这个Pointer指向了一张函数表,这张函数表中的每一项就是JNI中的一个函数的入口。本地的方法通过查找这张表来调用某个jni函数,来和JVM交互。如图:
第二个参数,如果申明的方法是static类型的,则该参数是一个jclass类型,如果申明的方法是一个非static类型的,则该参数是一个jobject类型。
如果你的方法含有参数,那么从第三个参数开始,就是申明时方法的参数了。