从Java到C++:JNI基本概念

469 阅读4分钟

从Java到C++系列目录

前言

JNI(Java Native Interface):是一组Native编程接口,用来实现Java(Kotlin亦可)与其他编程语言(例如 C、C++ 和汇编)的交互。

通过JNI,我们将一些重要代码下沉到C/C++层,提高反编译的难度。

通过JNI,我们可以调用C/C++一些成熟的、高性能的第三方库。如:调用OpenCV进行图像处理、调用FFMPEG进行音视频处理等。

JNI其实是用C/C++实现的,写JNI的时候,其实就是在写C/C++代码,要遵守C/C++的语法。

JNI主要的工作是为我们建立Java与C/C++之间的映射。下面主要介绍Java对应的JNI概念(了解即可,不需要专门记忆):

基本类型

引用类型

方法和字段

类型签名

基本类型

Java TypeNative TypeDescription
booleanjbooleanunsigned 8 bits
bytejbytesigned 8 bits
charjcharunsigned 16 bits
shortjshortsigned 16 bits
intjintsigned 32 bits
longjlongsigned 64 bits
floatjfloat32 bits
doublejdouble64 bits
voidvoidN/A

注意:

jboolean、jbyte等,本质上就是C++里的基本类型。在头文件jni.h中,声明如下:

typedef uint8_t  jboolean; /* unsigned 8 bits */
typedef int8_t   jbyte;    /* signed 8 bits */
typedef uint16_t jchar;    /* unsigned 16 bits */
typedef int16_t  jshort;   /* signed 16 bits */
typedef int32_t  jint;     /* signed 32 bits */
typedef int64_t  jlong;    /* signed 64 bits */
typedef float    jfloat;   /* 32-bit IEEE 754 */
typedef double   jdouble;  /* 64-bit IEEE 754 */

引用类型

JNI提供了与各种Java引用类型相对应的引用类型。JNI引用类型按照下图左侧的层次结构组织(右侧是对应的Java引用类型):

jobject                          Object 
├─jclass                         ├─java.lang.Class
├─jstring                        ├─java.lang.String
├─jarray                         │
│  ├─jobjectArray                ├─Object[]
│  ├─jbooleanArray               ├─boolean[]
│  ├─jbyteArray                  ├─byte[]
│  ├─jcharArray                  ├─char[]
│  ├─jshortArray                 ├─short[]
│  ├─jintArray                   ├─int[]
│  ├─jlongArray                  ├─long[]
│  ├─jfloatArray                 ├─float[]
│  └─jdoubleArray                ├─double[]
└─jthrowable                     └─java.lang.Throwable

注意:

1.Java的引用类型,对应的JNI类型为jobject。即Object、Thread、ArrayList这些引用类型,在JNI中都是jobject。

2.Java基本类型的数组,都有专属的JNI类型。如boolean[]对应jbooleanArray、byte[]对应jbyteArray。

3.Java引用类型的数组,对应的JNI类型均为jobjectArray。如Point[]、Integer[]等。

4.jstring、jclass、jarray和jthrowable都可以向上转型为jobject。

5.jobject向下转型为jstring、jclass、jarray和jthrowable也是可行的。哪怕类型不符合,编译器也不会报错,甚至运行也可能正常(如果是Java,会编译错误)。

因为jobject、jclass等,本质上就是C/C++里的指针。而不同类型的指针变量的值,在内存中本质上都是一样的,都是一个整数值的地址值。在头文件jni.h中,声明如下:

在C中,其他的JNI引用类型都被定义为jobject。

//jobject只是void*的别名。void*是无类型指针,可以指向任意类型的数据。
typedef void*           jobject;
//jclass只是jobject的别名。
typedef jobject         jclass;
typedef jobject         jstring;
...

在C++中,JNI使用一组类,来建立类似Java的层次结构。

class _jobject {};
class _jclass : public _jobject {};
class _jstring : public _jobject {};
...

//jobject只是_jobject*的别名。
typedef _jobject*       jobject;
typedef _jclass*        jclass;
typedef _jstring*       jstring;
...

方法和字段

Native TypeDescription
jmethodID表示Java的一个方法,成员方法或静态方法
jfieldID表示Java的一个字段,成员字段或静态字段

jmethodId、jfieldID本质也是指针。在头文件jni.h中,声明如下:

struct _jfieldID;                       
typedef struct _jfieldID* jfieldID;

struct _jmethodID;
typedef struct _jmethodID* jmethodID;

类型签名

JNI复用了Java虚拟机的类型签名。熟悉Java字节码或者Smali的人,对相关类型签名应该不陌生。不熟悉的,可以这么理解:类型签名就是能唯一描述某个Java类型或方法的一个字符串。各个类型签名与Java类型的对应关系如下表:

类型签名Java类型
Zboolean
Bbyte
Cchar
Sshort
Iint
Jlong
Ffloat
Ddouble
L fully-qualified-class ;fully-qualified-class
[ typetype[]
( arg-types ) ret-typemethod type

注意:

1.fully-qualified-class指的是Java引用类型的全限定名。如String类型,就是java/lang/String,对应的类型签名是Ljava/lang/String;(String后面还有个分号)。

2.type[] 指的是Java数组。该类型签名的规则是"[ + 数组元素的类型签名"。如byte数组,就是byte[],对应的类型签名是[B。String数组,就是String[],对应的类型签名是[Ljava/lang/String;

3.method type指的是方法。对应的类型签名 ( arg-types ) ret-type,返回值是后置的。如:

Java方法:

long f (int n, String s, int[] arr);

对应的类型签名:

(ILjava/lang/String;[I)J 

参考资料

Oracle的Java Native Interface Specification