前言
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概念(了解即可,不需要专门记忆):
基本类型
注意:
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 Type | Description |
|---|---|
| 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类型 |
|---|---|
| Z | boolean |
| B | byte |
| C | char |
| S | short |
| I | int |
| J | long |
| F | float |
| D | double |
| L fully-qualified-class ; | fully-qualified-class |
| [ type | type[] |
| ( arg-types ) ret-type | method 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