JNI接口
JNI是Java Native Interface的英文缩写,类似一种标准,提供了很多的API,使Java可以和C/C++进行通信。而JNI接口正是Java层世界与C/C++语言世界之间的桥梁。
探究:为什么使用JNI呢?
1.使用现有的开源库,现在很多优秀的开源库都是用C/C++编写的。
2.代码的保护,Android APK的Java代码容易被反编译,而C/C++更难 反编译。
3.便于移植,用C/C++写的库可以方便在其他嵌入式平台使用。
一:通过JNI接口实现Java层与Native层相互调用
现在来看下面这一张图,通过这张图完美的展示JNI接口的规则。
首先看到这张图片左边部分是熟悉的Java层开发,中间部分是要用到JNI接口来调用相关的C/C++函数。那么C/C++是怎么调用Java代码的呢?这就要用到反射,在JNI接口中提供了很多函数供我们进行调用。
二:JNI接口
JNI接口是谷歌开发人员为了方便Java层与C/C++层进行通信而对外提供的接口,对应的接口描述文档在“jni.h”头文件中。
这一排就是JNI接口中的数据类型,数据类型就是在基本数据类型前加了一个字母j而已,记忆起来比较简单。如下图所示。
紧接着是对象、数组等类型,如下图所示。
接下来介绍JNI函数,包含的有以下几种类型,其中Call开头的函数字面意思就是调用,这里指的是通过反射调用Java层方法;Get开头的函数字面意思就是获取,这里指的是通过反射获取Java层字段的值;Set开头的函数字面意思就是设置,这里指的是通过反射修改Java层字段的值;New开头的就是创建其他类型的函数。
jclass (FindClass)(JNIEnv, const char*);
jmethodID (GetMethodID)(JNIEnv, jclass, const char*, const char*);
jobject (CallObjectMethod)(JNIEnv,jobject,jmethodID, …);
jfieldID (GetFieldID)(JNIEnv, jclass, const char*, const char*);
jobject (GetObjectField)(JNIEnv, jobject, jfieldID);
void (SetObjectField)(JNIEnv, jobject, jfieldID, jobject);
jmethodID(GetStaticMethodID)(JNIEnv,jclass,const char*,constchar*);
jobject (CallStaticObjectMethod)(JNIEnv,jclass, jmethodID, …);
jfieldID (GetStaticFieldID)(JNIEnv,jclass,const char*,const char*);
jobject (GetStaticObjectField)(JNIEnv, jclass, jfieldID);
void (SetStaticObjectField)(JNIEnv, jclass, jfieldID, jobject);
jstring (NewStringUTF)(JNIEnv, const char*);
接下来对上面的JNI接口函数进行了解。
(1)调用Java层普通方法
Jobject:返回值,这里返回的是jobject 。
(*CallObjectMethod):这里调用的是普通方法。
(JNIEnv*,jobject,jmethodID, …):参数列表信息。
第一参数是JNIEnv*,是本地调用接口,里面提供大量的JNI接口函数供调用。第二个参数是jobject对象。 第三个参数是方法ID,该参数可以通过GetMethodID函数获取。“…”表示的是调用方法的参数列表信息。
探究:什么是Get MethodID?
使用GetMethodID函数获取Java层的方法ID,原形如下:
jmethodID:返回值,这里返回的是Java层方法ID。
(*GetMethodID):获取普通方法ID。
(JNIEnv*, jclass, const char*, const char*):参数列表信息。
该函数的第二个参数是jclass,可以通过FindClass函数获取。第三个参数是Java层的方法名称,第四参数是Java层方法的签名信息。
探究:什么是FindClass?
FindClass函数通过反射获取Java类,原形如下:
Jclass:返回值,这里返回的是java类。
(*FindClass):反射获取Java类。
(JNIEnv*, const char*):参数列表信息。
(2)获取Java层实例字段的值
Jobject:返回值,这里返回的是jobject 。
(*GetObjectField):获取Java层实例字段的值。
(JNIEnv*, jobject, jfieldID):参数列表信息。第三参数是实例字段ID,该参数可以通过GetFieldID函数获取,原形如下:
jfieldID:返回值,这里返回的是实例字段ID。
(*GetFieldID):获取实例字段ID。
(JNIEnv*, jclass, const char*, const char*):参数信息。该函数的第三个参数是实例字段名称,第四个参数是实例字段的签名信息。
(3)设置Java层实例字段的值
Void:返回值为void。
(*SetObjectField):设置Java层实例字段的值。
(JNIEnv*, jobject, jfieldID, jobject):参数信息。第四个参 数为Java层实例字段要设置的值。
(4)调用Java层静态方法
Jobject:返回值,这里返回的是jobject 。
(*CallStaticObjectMethod):这里调用的是静态方法。
(JNIEnv*,jclass, jmethodID, …):参数列表信息。第一参数是JNIEnv*,是本地调用接口,里面提供大量的JNI接口函数供我们调用。第二个参数是jclass。第三个参数是静态方法ID,该参数可以通过GetStatic MethodID函数获取。后面“…”表示的是调用方法的参数列表信息。
探究:什么是GetStaticMethodID?
使用GetStaticMethodID函数获取Java层的静态方法ID,原形如下:
jmethodID :返回值,这里返回的是Java层方法ID。
(*GetStaticMethodID):获取静态方法ID。
(JNIEnv*, jclass, const char*, const char*):参数列表信息。
该函数的第二个参数是jclass,可以通过FindClass函数获取。第三个参数是Java层的方法名称,第四参数是Java层方法的签名信息。
(5)获取Java层静态字段的值
Jobject:返回值,这里返回的是jobject 。
(*GetStaticObjectField):获取Java层静态字段的值。
(JNIEnv*, jclass, jfieldID): 参数列表信息。第三参数是静态字段ID,该参数可以通过GetStaticFieldID函数获取,原形如下:
jfieldID: 返回值,这里返回的是实例字段ID。
(*GetStaticFieldID): 获取静态字段ID。
(JNIEnv*, jclass, const char*, const char*): 参数信息。该函数的第三个参数是静态字段名称,第四个参数是静态字段的签名信息。
(6)设置Java层静态字段的值
void : 返回值为void。
(*SetStaticObjectField): 设置Java层静态字段的值。
(JNIEnv*, jclass, jfieldID, jobject):参数信息。第四个参数为Java层静态字段要设置的值。
(7)New开头的函数就是创建,借助new开头的函数api创建相应的基本类型、数组、对象,原形:
Jstring:返回值为jstring。
(*NewStringUTF) :创建utf-8编码的字符串。
(JNIEnv*, const char*):参数列表信息。第二个就是参数是char*类型的值,在这里可以输入想要创建的字符串。
小结:
今天主要分享了JNI接口是实现Java层与C/C++层相互调用,以及常用的JNI接口函数和它们的用途,如下表所示。