JNI

  • 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是两者相互沟通的中介。

  • 几个关键词
  1.  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:\jniResearch\MyApplication\app\src\main\java\test\hank\com\jni20180205\QPlayer.java
运行之后,就会在QPlayer.java的旁边,生成一个QPlayer.class.

javah:如图,

1)切换到java目录(cd F:\jniResearch\MyApplication\app\src\main\java)

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++开发人员来写。

  

posted @ 2018-02-06 10:54  波澜不惊x  阅读(335)  评论(0编辑  收藏  举报