vlambda博客
学习文章列表

Java 和 c/c++之间是利用什么工具进行通信的?



点击蓝字
默默关注


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++写的库可以方便在其他嵌入式平台使用。


1
通过JNI接口实现Java层与Native层相互调用


现在来看下面这一张图,通过这张图完美的展示JNI接口的规则。              

首先看到这张图片左边部分是熟悉的Java层开发,中间部分是要用到JNI接口来调用相关的C/C++函数。那么C/C++是怎么调用Java代码的呢?这就要用到反射,在JNI接口中提供了很多函数供我们进行调用。


2
JNI接口


JNI接口是谷歌开发人员为了方便Java层与C/C++层进行通信而对外提供的接口,对应的接口描述文档在“jni.h”头文件中。

这一排就是JNI接口中的数据类型,数据类型就是在基本数据类型前加了一个字母j而已,记忆起来比较简单。如图1.1所示。


Java 和 c/c++之间是利用什么工具进行通信的?

                               (图1.1)


紧接着是对象、数组等类型,如图1.2所示。


Java 和 c/c++之间是利用什么工具进行通信的?

                               (图1.2)


接下来介绍JNI函数,包含的有以下几种类型,其中Call开头的函数字面意思就是调用,这里指的是通过反射调用Java层方法;Get开头的函数字面意思就是获取,这里指的是通过反射获取Java层字段的值;Set开头的函数字面意思就是设置,这里指的是通过反射修改Java层字段的值;New开头的就是创建其他类型的函数。


1. jclass (*FindClass)(JNIEnv*, const char*);


2. jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);


3. jobject(*CallObjectMethod)(JNIEnv*,jobject,jmethodID, ...);


4. jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);


5. jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID); 


6. void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);


7. jmethodID(*GetStaticMethodID)(JNIEnv*,jclass,const char*,constchar*);


8. jobject (*CallStaticObjectMethod)(JNIEnv*,jclass, jmethodID, ...);


9. jfieldID (*GetStaticFieldID)(JNIEnv*,jclass,const char*,const char*);


10. jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);


11. void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);


12. 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*类型的值,在这里可以输入想要创建的字符串。



3
小结:


本次主要分享了JNI接口是实现Java层与C/C++层相互调用,以及常用的JNI接口函数和它们的用途,如下表所示。




文章推荐