- JNI的存在价值:
Java Native Interface(JNI)是Java语言的本地编程接口,是J2SDK的一部分。在java程序中,我们可以通过JNI实现一些用java语言不便实现的功能。通常有以下几种情况我们需要使用JNI来实现。
- 标准的java类库没有提供你的应用程序所需要的功能,通常这些功能是平台相关的(如某些平台特有的功能)。
- 你希望使用一些已经有的类库或者应用程序,而他们并非用java语言编写的(如:已有的c++功能库)
- 程序的某些部分对速度要求比较苛刻,你选择用汇编或者c语言来实现并在java语言中调用他们(要求执行效率高的,java无法达到那种效率)
不到万不得已不要使用JNI技术,一方面它需要你掌握更多的知识才可以驾驭(作为一个安卓开发者,要同时兼顾android以及c++开发,还是很难的),
一方面使用了JNI你的程序就会丧失可移植性(因为有一部分功能是jni写的,用到了其他平台的特性,导致不能在任何平台上都能有一样的效果,就失去了跨平台的特性)。
如果一句话概括JNI的存在意义:就是,让java调用c++以及c++回调java,jni是两者相互沟通的中介。
- 几个关键词
- cpp编程一般是基于头文件.h进行的,那么如何生成javaJni类对应的.h文件呢?
javac,javah
javac是将一个java文件编译成.class,javah则是将.class转化成.h. 这个.h头文件。之后,cpp按照这个h文件来编程,用c++代码来实现jni函数。
下面以一个androidStudio project作为案例来看:
javac:
如果你要将一个QPlayer.java文件编译成QPlayer.class.
cmd命令写法为:javac 后面接上QPlayer.java文件的完整路径。
如:javac F:jniResearchMyApplicationappsrcmainjava esthankcomjni20180205QPlayer.java
运行之后,就会在QPlayer.java的旁边,生成一个QPlayer.class.
javah:如图,
1)切换到java目录(cd F:jniResearchMyApplicationappsrcmainjava)
2)执行cmd:
javah test.hank.com.jni20180205.QPlayer
.h文件就会出现在java目录下。
这个过程也称为静态注册Native方法。
2.java和c++是两种不同的语言,他们的数据类型也不相同,那么JNI如何做到两者数据互通?
既然JNI可以沟通java和c++,那么他们两个的数据类型 不相同该怎么处理呢?JNI的方式是,使用一个中介。
如图,java类型,本地类型(即c++类型),这里还有一个叫:JNI别名。
我们看一下1)中生成的.h文件。以及原始的.java文件。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class test_hank_com_jni20180205_QPlayer */ #ifndef _Included_test_hank_com_jni20180205_QPlayer #define _Included_test_hank_com_jni20180205_QPlayer #ifdef __cplusplus extern "C" { #endif /* * Class: test_hank_com_jni20180205_QPlayer * Method: add * Signature: ()I */ JNIEXPORT jint JNICALL Java_test_hank_com_jni20180205_QPlayer_add (JNIEnv *, jobject); /* * Class: test_hank_com_jni20180205_QPlayer * Method: getString * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_test_hank_com_jni20180205_QPlayer_getString (JNIEnv *, jobject); /* * Class: test_hank_com_jni20180205_QPlayer * Method: test1 * Signature: (IJLjava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_test_hank_com_jni20180205_QPlayer_test1 (JNIEnv *, jobject, jint, jlong, jstring); #ifdef __cplusplus } #endif #endif |
public class QPlayer { static { System.loadLibrary("qplayer");//这里,真实加载的文件其实是:libQplayer.so } public native int add(); public native String getString(); public native String test1(int a,long b,String c); } |
可以发现,java中的test1函数,三个参数分别是int,long,String,c++的h文件中,test1函数对应位置则是jint,jlong,jstring,这就是所谓的jni别名. 然而这里的jint,jlong,jstring并不是c++的int,long和string,而是jni.h中特别定制的(h文件最上面#include <jni.h>,说明引用了jni.h).
3. 步骤1中说的是用java的jni类来生成头文件,然后用这个头文件来约束c++编程规范。那如果真实情况下,没有这个java jni类呢?也有办法:native方法动态注册,不依赖java jni类。
这部分需要对c++想当熟悉,笔者没做过c++相关工作,具体请看:https://www.jianshu.com/p/aba734d5b5cd,搜索关键字 动态注册。一般这部分工作还是由c++开发人员来写。