|
|
@@ -0,0 +1,565 @@
|
|
|
+# JNI(Java Native Interface)的参数传递和返回
|
|
|
+
|
|
|
+### Java数据类型与C/C++本地数据类型的映射关系
|
|
|
+
|
|
|
+- Java的基本数据类型表示映射到C/C++的表示都是在原来的标识字符加上字符"j"
|
|
|
+
|
|
|
+> 例如:Java的int类型在C/C++中表示为jint类型
|
|
|
+
|
|
|
+- 对象类型使用jobject表示
|
|
|
+
|
|
|
+- "void"的表示在Java和C/C++中保持一致
|
|
|
+
|
|
|
+在头文件"jni_md.h"中有如下定义
|
|
|
+
|
|
|
+```
|
|
|
+ typedef long jint;
|
|
|
+ typedef __int64 jlong;
|
|
|
+ typedef signed char jbyte;
|
|
|
+```
|
|
|
+
|
|
|
+> 头文件"jni_md.h"在JDk的"include/linux"目录下
|
|
|
+
|
|
|
+在头文件"jni.h"中有如下定义
|
|
|
+
|
|
|
+```
|
|
|
+ typedef unsigned char jboolean;
|
|
|
+ typedef unsigned short jchar;
|
|
|
+ typedef short jshort;
|
|
|
+ typedef float jfloat;
|
|
|
+ typedef double jdouble;
|
|
|
+ typedef jint jsize;
|
|
|
+```
|
|
|
+
|
|
|
+> 头文件"jni.h"在JDk的"include"目录下
|
|
|
+
|
|
|
+映射关系如下表
|
|
|
+
|
|
|
+| Java类型 | 本地类型(C/C++类型) | JNI中定义的别名 | 描述 |
|
|
|
+| -------- | ------------------- | --------------- | ---------------- |
|
|
|
+| int | long | jint | 有符号的32位表示 |
|
|
|
+| long | _int64 | jlong | 有符号的64位表示 |
|
|
|
+| byte | signed char | jbyte | 有符号的8位表示 |
|
|
|
+| boolean | unsigned char | jboolean | 无符号的8位表示 |
|
|
|
+| char | unsigned short | jchar | 无符号16位表示 |
|
|
|
+| short | short | jshort | 有符号16位表示 |
|
|
|
+| float | float | jfloat | 32位表示 |
|
|
|
+| double | double | jdouble | 64位表示 |
|
|
|
+| Object | _jobject* | jobject | 对象类型 |
|
|
|
+| void | void | void | |
|
|
|
+
|
|
|
+Java基本数据类型中除了boolean类型,其他数据类型都可以直接转换为C/C++的基本数据类型
|
|
|
+
|
|
|
+在C/C++中使用常量"JNI_TRUE"表示"true",常量"JNI_FALSE"表示"false"
|
|
|
+
|
|
|
+> ```
|
|
|
+> //jint类型可以直接当作int类型使用
|
|
|
+> int a;
|
|
|
+> jint b = a;
|
|
|
+> long c = b;
|
|
|
+> //jboolean使用unsigned char表示
|
|
|
+> //常量"JNI_TRUE"表示"true"
|
|
|
+> //常量"JNI_FALSE"表示"false"
|
|
|
+> jboolean ztrue = JNI_TRUE;
|
|
|
+> jboolean zfasle = JNI_FALSE;
|
|
|
+> ```
|
|
|
+
|
|
|
+### Java数据类型的类型签名表示(与Java VM Type Signatures规范有关)
|
|
|
+
|
|
|
+> JNI中进行对象相关的操作时需要用到Java数据类型的符号表示
|
|
|
+
|
|
|
+Java数据类型与类型签名映射关系表
|
|
|
+
|
|
|
+| Type Signature(类型签名) | Java Type(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` |
|
|
|
+| V | void |
|
|
|
+
|
|
|
+#### 类型签名案例
|
|
|
+
|
|
|
+- `int`类型的类型签名为:`I`
|
|
|
+- `boolean`类型的类型签名为:`D`
|
|
|
+- `java.lang.String`类型的类型签名为:`Ljava/lang/String`
|
|
|
+
|
|
|
+> "L"表示为对象类型,"Ljava/lang/String"表示具体的对象类型为"java.lang.String"
|
|
|
+
|
|
|
+- `int[]`类型的类型签名为:`[I`
|
|
|
+
|
|
|
+> 一个左中括号"["表示为一维数组,完整"[int"表示为int类型的一维数组
|
|
|
+
|
|
|
+- `int[][]`类型的类型签名为:`[[I`
|
|
|
+
|
|
|
+> 两个左中括号"[["表示为二维数组,完整"[[nt"表示为int类型的二维数组
|
|
|
+>
|
|
|
+> 总结:有多少个左中括号就表示是多少维数组
|
|
|
+
|
|
|
+- `java.lang.String[]`类型的类型签名为:`[Ljava/lang/String`
|
|
|
+
|
|
|
+- 例如,有如下方法`public static void main(String[] agrs);`对应的方法类型签名为:`([Ljava/lang/String;)V`
|
|
|
+
|
|
|
+> - "()":一对小括号表示方法参数列表
|
|
|
+> - "[":一个左中括号表示一维数组
|
|
|
+> - "L":表示对象类型
|
|
|
+> - "java/lang/String":表示类"java.lang.String"
|
|
|
+> - "Ljava/lang/String;":表示"java.lang.String"这个对象类型
|
|
|
+> - "V":在这里表示返回值,返回类型为void
|
|
|
+>
|
|
|
+> 小括号外的类型签名表示方法的返回值类型
|
|
|
+>
|
|
|
+> 组合在一起的完整含义为,接收一个参数为字符串数组,返回值为void类型的方法
|
|
|
+
|
|
|
+- 再例如`int sum(int a,int b);`对应的类型签名为`(II)I`
|
|
|
+
|
|
|
+> 含义为有两个参数类型为int,返回值类型为int的方法
|
|
|
+
|
|
|
+- `String method(String str1,String str2);`对应的类型签名为`(Ljava/lang/String;Ljava/lang/String)Ljava/lang/String`
|
|
|
+
|
|
|
+> 参数列表有多个对象类型的参数时,对象类型的类型签名结尾需要使用分号分割
|
|
|
+
|
|
|
+### 案例:基本类型的参数传递和返回
|
|
|
+
|
|
|
+- Java代码
|
|
|
+
|
|
|
+```
|
|
|
+ package space.anyi.jni.parameter;
|
|
|
+
|
|
|
+ public class Main {
|
|
|
+ static {
|
|
|
+ System.load("/home/yangyi/JNI-learn/c/parameter/JNI_parameter.so");
|
|
|
+ }
|
|
|
+ public static void main(String[] args) {
|
|
|
+ BaseTypeTest baseTypeTest = new BaseTypeTest();
|
|
|
+ int sum = baseTypeTest.sum(1, 2);
|
|
|
+ System.out.println(sum);
|
|
|
+ boolean flag = baseTypeTest.booleanTypeTest(false);
|
|
|
+ System.out.println(flag);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ class BaseTypeTest {
|
|
|
+ //定义一个有参数和返回值的native方法
|
|
|
+ native int sum(int val1,int val2);
|
|
|
+ //boolean类型的测试
|
|
|
+ native boolean booleanTypeTest(boolean flag);
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+- 生成的头文件内容
|
|
|
+
|
|
|
+生成头文件的命令:`javac -h c/parameter src/main/java/space/anyi/jni/parameter/Main.java`
|
|
|
+
|
|
|
+```
|
|
|
+ /* DO NOT EDIT THIS FILE - it is machine generated */
|
|
|
+ #include <jni.h>
|
|
|
+ /* Header for class space_anyi_jni_parameter_BaseTypeTest */
|
|
|
+
|
|
|
+ #ifndef _Included_space_anyi_jni_parameter_BaseTypeTest
|
|
|
+ #define _Included_space_anyi_jni_parameter_BaseTypeTest
|
|
|
+ #ifdef __cplusplus
|
|
|
+ extern "C" {
|
|
|
+ #endif
|
|
|
+ /*
|
|
|
+ * Class: space_anyi_jni_parameter_BaseTypeTest
|
|
|
+ * Method: sum
|
|
|
+ * Signature: (II)I
|
|
|
+ */
|
|
|
+ JNIEXPORT jint JNICALL Java_space_anyi_jni_parameter_BaseTypeTest_sum
|
|
|
+ (JNIEnv *, jobject, jint, jint);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Class: space_anyi_jni_parameter_BaseTypeTest
|
|
|
+ * Method: booleanTypeTest
|
|
|
+ * Signature: (Z)Z
|
|
|
+ */
|
|
|
+ JNIEXPORT jboolean JNICALL Java_space_anyi_jni_parameter_BaseTypeTest_booleanTypeTest
|
|
|
+ (JNIEnv *, jobject, jboolean);
|
|
|
+
|
|
|
+ #ifdef __cplusplus
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+- 本地方法的实现
|
|
|
+
|
|
|
+```
|
|
|
+ #include "space_anyi_jni_parameter_BaseTypeTest.h"
|
|
|
+ #include <stdio.h>
|
|
|
+ int sum(int a,int b){
|
|
|
+ return a+b;
|
|
|
+ }
|
|
|
+ JNIEXPORT jint JNICALL Java_space_anyi_jni_parameter_BaseTypeTest_sum(JNIEnv *env, jobject object, jint val1, jint val2){
|
|
|
+ //jint类型直接当作int类型使用
|
|
|
+ int temp = sum(val1,val2);
|
|
|
+ jint result = temp;
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ JNIEXPORT jboolean JNICALL Java_space_anyi_jni_parameter_BaseTypeTest_booleanTypeTest(JNIEnv *env, jobject object, jboolean flag){
|
|
|
+ //jboolean类型当作unsigned char类型使用
|
|
|
+ unsigned char val = flag;
|
|
|
+ printf("%d\n",val);
|
|
|
+ //在C/C++中使用常量"JNI_TRUE"表示"true",常量"JNI_FALSE"表示"false"
|
|
|
+ return val ? JNI_FALSE : JNI_TRUE;
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+- 编译为动态链接库
|
|
|
+
|
|
|
+中间编译
|
|
|
+
|
|
|
+```
|
|
|
+gcc -c -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -o c/parameter/JNI_parameter.o c/parameter/JNI_parameter.c
|
|
|
+```
|
|
|
+
|
|
|
+动态链接库编译
|
|
|
+
|
|
|
+```
|
|
|
+gcc -shared -fPIC -o c/parameter/JNI_parameter.so c/parameter/JNI_parameter.o -lc
|
|
|
+```
|
|
|
+
|
|
|
+- 运行测试:`java -cp src/main/java space.anyi.jni.parameter.Main`
|
|
|
+
|
|
|
+输出结果
|
|
|
+
|
|
|
+```
|
|
|
+ yangyi@debain12:~/JNI-learn$ java -cp src/main/java space.anyi.jni.parameter.Main
|
|
|
+ 3
|
|
|
+ 0
|
|
|
+ true
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### 案例:引用类型(对象类型)的参数传递和返回
|
|
|
+
|
|
|
+JNI中关于对象的操作与Java的类反射机制非常相似
|
|
|
+
|
|
|
+JNI中对象抽象与Java类反射机制抽象的映射表
|
|
|
+
|
|
|
+| Java 类型反射抽象 | JNI 类型/句柄 | 说明 |
|
|
|
+| -------------------------- | ------------------------- | ------------------------------------------------------------ |
|
|
|
+| `java.lang.Class` | `jclass` | 表示 Java 类对象,可通过 `FindClass`、`GetObjectClass` 等获取 |
|
|
|
+| `java.lang.reflect.Method` | `jmethodID` | 表示 Java 方法,可通过 `GetMethodID`、`GetStaticMethodID` 获取 |
|
|
|
+| `java.lang.reflect.Field` | `jfieldID` | 表示 Java 字段,可通过 `GetFieldID`、`GetStaticFieldID` 获取 |
|
|
|
+| `java.lang.Object` | `jobject` | 表示 Java 对象实例的通用引用 |
|
|
|
+| `java.lang.String` | `jstring` | 表示 Java 字符串对象,是 `jobject` 的子类型 |
|
|
|
+| `java.lang.Throwable` | `jthrowable` | 表示 Java 异常对象,是 `jobject` 的子类型 |
|
|
|
+| 数组类型(如 `Object[]`) | `jarray` / `jobjectArray` | 通用数组句柄,具体类型如 `jintArray`、`jobjectArray` 等 |
|
|
|
+
|
|
|
+- Java代码,通过本地方法获取一个对象(对象类型的返回),通过本地方法输出一个对象的信息(对象类型的入参)
|
|
|
+
|
|
|
+```
|
|
|
+ package space.anyi.jni.parameter;
|
|
|
+
|
|
|
+ import java.util.Arrays;
|
|
|
+
|
|
|
+ public class Main {
|
|
|
+ static {
|
|
|
+ System.load("/home/yangyi/JNI-learn/c/parameter/JNI_object_parameter.so");
|
|
|
+ }
|
|
|
+ public static void main(String[] args) {
|
|
|
+
|
|
|
+ //JNI对象数据类型的传递
|
|
|
+ ObjectTest objectTest = new ObjectTest();
|
|
|
+ Student student = objectTest.getStudent();
|
|
|
+ System.out.println(student);
|
|
|
+ objectTest.printStudentInfo(student);
|
|
|
+ System.out.println(student);
|
|
|
+ // objectTest.free(student);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ class ObjectTest{
|
|
|
+ /**
|
|
|
+ * 通过JNI获取对象,返回的是全局的JNI引用,需要手动管理,有内存泄露的风险
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ native Student getStudent();
|
|
|
+ native void printStudentInfo(Student student);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 释放JNI创建的全局引用
|
|
|
+ * @param student
|
|
|
+ */
|
|
|
+ native void free(Student student);
|
|
|
+ }
|
|
|
+ class Student{
|
|
|
+ private String name;
|
|
|
+ private int age;
|
|
|
+ private float height;
|
|
|
+ private int[] source;
|
|
|
+ private String[] hobby;
|
|
|
+
|
|
|
+ public Student(String name) {
|
|
|
+ this.name = name;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int getAge() {
|
|
|
+ return age;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setAge(int age) {
|
|
|
+ this.age = age;
|
|
|
+ }
|
|
|
+
|
|
|
+ public float getHeight() {
|
|
|
+ return height;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setHeight(float height) {
|
|
|
+ this.height = height;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int[] getSource() {
|
|
|
+ return source;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setSource(int[] source) {
|
|
|
+ this.source = source;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String[] getHobby() {
|
|
|
+ return hobby;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setHobby(String[] hobby) {
|
|
|
+ this.hobby = hobby;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getName() {
|
|
|
+ return name;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setName(String name) {
|
|
|
+ this.name = name;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void info(){
|
|
|
+ System.out.println("My name is %s,i am %d year old,height:%f".formatted(this.name,this.age,this.height));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ return "Student{" +
|
|
|
+ "name='" + name + '\'' +
|
|
|
+ ", age=" + age +
|
|
|
+ ", height=" + height +
|
|
|
+ ", source=" + Arrays.toString(source) +
|
|
|
+ ", hobby=" + Arrays.toString(hobby) +
|
|
|
+ '}';
|
|
|
+ }
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+- 编译头文件:`javac -h c/parameter/ src/main/java/space/anyi/jni/parameter/Main.java`
|
|
|
+
|
|
|
+得到文件"space_anyi_jni_parameter_ObjectTest.h"
|
|
|
+
|
|
|
+```
|
|
|
+ /* DO NOT EDIT THIS FILE - it is machine generated */
|
|
|
+ #include <jni.h>
|
|
|
+ /* Header for class space_anyi_jni_parameter_ObjectTest */
|
|
|
+
|
|
|
+ #ifndef _Included_space_anyi_jni_parameter_ObjectTest
|
|
|
+ #define _Included_space_anyi_jni_parameter_ObjectTest
|
|
|
+ #ifdef __cplusplus
|
|
|
+ extern "C" {
|
|
|
+ #endif
|
|
|
+ /*
|
|
|
+ * Class: space_anyi_jni_parameter_ObjectTest
|
|
|
+ * Method: getStudent
|
|
|
+ * Signature: ()Lspace/anyi/jni/parameter/Student;
|
|
|
+ */
|
|
|
+ JNIEXPORT jobject JNICALL Java_space_anyi_jni_parameter_ObjectTest_getStudent
|
|
|
+ (JNIEnv *, jobject);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Class: space_anyi_jni_parameter_ObjectTest
|
|
|
+ * Method: printStudentInfo
|
|
|
+ * Signature: (Lspace/anyi/jni/parameter/Student;)V
|
|
|
+ */
|
|
|
+ JNIEXPORT void JNICALL Java_space_anyi_jni_parameter_ObjectTest_printStudentInfo
|
|
|
+ (JNIEnv *, jobject, jobject);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Class: space_anyi_jni_parameter_ObjectTest
|
|
|
+ * Method: free
|
|
|
+ * Signature: (Lspace/anyi/jni/parameter/Student;)V
|
|
|
+ */
|
|
|
+ JNIEXPORT void JNICALL Java_space_anyi_jni_parameter_ObjectTest_free
|
|
|
+ (JNIEnv *, jobject, jobject);
|
|
|
+
|
|
|
+ #ifdef __cplusplus
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+ #endif
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+- 实现本地方法
|
|
|
+
|
|
|
+> C语言与C++语言使用env指针的API不一样,C语言比C++语言版本的API多一对小括号和多传递一个参数
|
|
|
+>
|
|
|
+> ```
|
|
|
+> //C语言
|
|
|
+> (*env)->functionName(env,args...)
|
|
|
+> //C++语言
|
|
|
+> env->functionName(args...)
|
|
|
+> ```
|
|
|
+
|
|
|
+native方法中创建的对象,如果需要在JVM中使用,需要创建全局引用,并将全局引用返回给JVM
|
|
|
+
|
|
|
+> 通过NewGlobalRefh函数创建全局引用
|
|
|
+>
|
|
|
+> ```
|
|
|
+> //创建JNI全局引用,以便JVM可以使用JNI中创建的对象
|
|
|
+> jobject result = (*env)->NewGlobalRef(env,student);
|
|
|
+> return result;
|
|
|
+> ```
|
|
|
+>
|
|
|
+> 通过DeleteGlobalRef函数释放全局引用
|
|
|
+>
|
|
|
+> ```
|
|
|
+> (*env)->DeleteGlobalRef(env,ref);
|
|
|
+> ```
|
|
|
+
|
|
|
+```
|
|
|
+ #include "space_anyi_jni_parameter_ObjectTest.h"
|
|
|
+ JNIEXPORT jobject JNICALL Java_space_anyi_jni_parameter_ObjectTest_getStudent(JNIEnv *env, jobject this){
|
|
|
+ printf("start:getStudent\n");
|
|
|
+ //C语言与C++语言使用env指针的方式不一样,C语言比C++语言版本的api多一对小括号和多传递一个参数
|
|
|
+ //C语言:(*env)->functionName(env,args...)
|
|
|
+ //C++语言:env->functionName(args...)
|
|
|
+ //1.通过JNIEnv指针获取指定类的jclass,通过类的类型签名指定
|
|
|
+ jclass class_Student = (*env)->FindClass(env,"Lspace/anyi/jni/parameter/Student;");
|
|
|
+ //2.获取类的构造方法,第一个参数是类的jclass,第二个参数是方法名,第三个参数是方法的类型签名
|
|
|
+ //构造方法的名称为:<init>,构造方法的返回值为void
|
|
|
+ jmethodID method_constructor_Student = (*env)->GetMethodID(env,class_Student,"<init>","(Ljava/lang/String;)V");
|
|
|
+ //3.调用构造方法创建对象,第一个参数是类的jclass,第二参数为jmethodID,从第三个参数开始为方法的入参
|
|
|
+ jstring nameStr = (*env)->NewStringUTF(env, "yangyi");
|
|
|
+ jobject student = (*env)->NewObject(env,class_Student,method_constructor_Student,nameStr);
|
|
|
+ //4.获取类的普通方法,第一个参数是jclass,第二个参数是方法的名称,第三个参数方法的类型签名
|
|
|
+ jmethodID student_method_setAge = (*env)->GetMethodID(env,class_Student,"setAge","(I)V");
|
|
|
+ //5.调用类的普通方法,第一个参数是将要调用该方法实例对象的jobject,第二个参数是方法对应的jmethodID,从第三个参数开始为方法的入参
|
|
|
+ (*env)->CallObjectMethod(env,student,student_method_setAge,18);
|
|
|
+ //6.获取类的普通字段,第一个参数是类的jcalss,第二个参数是字段的名称,第三个参数是字段的类型签名
|
|
|
+ jfieldID student_field_height = (*env)->GetFieldID(env,class_Student,"height","F");
|
|
|
+ //7.给类的普通字段设置值,第一个参数是类实例对应的jobject,第二个参数是字段对应的jfieldID,第三个参数是要设置的目标值
|
|
|
+ (*env)->SetFloatField(env,student,student_field_height,1.66);
|
|
|
+ //8.释放局部引用资源
|
|
|
+ (*env)->DeleteLocalRef(env, class_Student);
|
|
|
+ //创建JNI全局引用,以便JVM可以使用JNI中创建的对象
|
|
|
+ jobject result = (*env)->NewGlobalRef(env,student);
|
|
|
+ printf("end:getStudent\n");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ JNIEXPORT void JNICALL Java_space_anyi_jni_parameter_ObjectTest_printStudentInfo(JNIEnv *env, jobject this, jobject student){
|
|
|
+ printf("start:printStudentInfo\n");
|
|
|
+ //1.调用对象的普通方法
|
|
|
+ //1.1获取jclass
|
|
|
+ jclass class_Student = (*env)->GetObjectClass(env,student);
|
|
|
+ //1.2获取jmethodID
|
|
|
+ jmethodID student_method_info = (*env)->GetMethodID(env,class_Student,"info","()V");
|
|
|
+ //1.3call
|
|
|
+ (*env)->CallObjectMethod(env,student,student_method_info);
|
|
|
+ //2.获取对象的字段值
|
|
|
+ //2.1获取jfieldID
|
|
|
+ jfieldID student_field_height = (*env)->GetFieldID(env,class_Student,"height","F");
|
|
|
+ //2.2获取对象字段对应的值
|
|
|
+ jfloat height = (*env)->GetFloatField(env,student,student_field_height);
|
|
|
+ printf("height:%f\n",height);
|
|
|
+ //基本类型的数组操作
|
|
|
+ //3.1创建数组
|
|
|
+ jint size = 3;
|
|
|
+ jintArray newArray = (*env)->NewIntArray(env, size);
|
|
|
+ //3.2设置数组中的值
|
|
|
+ //第一个参数为数组的jarray,第二个参数为数组的默认值
|
|
|
+ jint *source = (*env)->GetIntArrayElements(env, newArray, NULL);
|
|
|
+ source[0] = 90;
|
|
|
+ source[1] = 91;
|
|
|
+ source[2] = 92;
|
|
|
+ //3.3获取数组中的值
|
|
|
+ jint val1 = source[0];
|
|
|
+ jint val2 = source[1];
|
|
|
+ jint val3 = source[2];
|
|
|
+ //更新数组的修改,第一个参数为数组的jarray,第二个参数为C/C++中数组,第三个参数为模式(0:将数组拷贝到jarray并释放C/C++中数组的内存;1:拷贝数组但不释放内存;2:不拷贝但释放内存)
|
|
|
+ (*env)->ReleaseIntArrayElements(env, newArray, source,0);
|
|
|
+ //获取数组的长度
|
|
|
+ jsize length = (*env)->GetArrayLength(env, newArray);
|
|
|
+ jfieldID student_field_source = (*env)->GetFieldID(env,class_Student,"source","[I");
|
|
|
+ (*env)->SetObjectField(env,student,student_field_source,newArray);
|
|
|
+ //对象类型的数组操作
|
|
|
+ jclass class_String = (*env)->FindClass(env,"Ljava/lang/String;");
|
|
|
+ //第一个参数为数组的大小,第二个参数为对象的jclass,第三个参数为数组默认值
|
|
|
+ jobjectArray objectArray = (*env)->NewObjectArray(env, size,class_String,NULL);
|
|
|
+ //对象数组赋值
|
|
|
+ jint index = 0;
|
|
|
+ jstring hobby = (*env)->NewStringUTF(env,"学习");
|
|
|
+ //第一个参数为jarray,第二个参数为索引,第三个参数为值的jobject
|
|
|
+ (*env)->SetObjectArrayElement(env,objectArray,index,hobby);
|
|
|
+ //对象数组取值,第一个参数为jarray,第二个参数为索引
|
|
|
+ jobject value = (*env)->GetObjectArrayElement(env,objectArray,index);
|
|
|
+ jfieldID student_field_hobby = (*env)->GetFieldID(env,class_Student,"hobby","[Ljava/lang/String;");
|
|
|
+ (*env)->SetObjectField(env,student,student_field_hobby,objectArray);
|
|
|
+ //释放局部引用资源,可选
|
|
|
+ (*env)->DeleteLocalRef(env, class_Student);
|
|
|
+ printf("end:printStudentInfo\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ JNIEXPORT void JNICALL Java_space_anyi_jni_parameter_ObjectTest_free(JNIEnv *env, jobject this, jobject student){
|
|
|
+ printf("start:free\n");
|
|
|
+ if(student != NULL){
|
|
|
+ printf("释放全局引用\n");
|
|
|
+ (*env)->DeleteGlobalRef(env,student);
|
|
|
+ }
|
|
|
+ printf("end:free\n");
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+- 编译动态链接库
|
|
|
+
|
|
|
+```
|
|
|
+ gcc -c -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -o c/parameter/JNI_object_parameter.o c/parameter/JNI_object_parameter.c
|
|
|
+ gcc -shared -fPIC c/parameter/JNI_object_parameter.so c/parameter/JNI_object_parameter.o -lc
|
|
|
+```
|
|
|
+
|
|
|
+> 得到动态链接库"JNI_object_parameter.so"
|
|
|
+
|
|
|
+- 测试输出:`java -cp src/main/java/ space.anyi.jni.parameter.Main`
|
|
|
+
|
|
|
+```
|
|
|
+ yangyi@debain12:~/JNI-learn$ java -cp src/main/java/ space.anyi.jni.parameter.Main
|
|
|
+ start:getStudent
|
|
|
+ end:getStudent
|
|
|
+ Student{name='yangyi', age=18, height=1.66, source=null, hobby=null}
|
|
|
+ start:printStudentInfo
|
|
|
+ My name is yangyi,i am 18 year old,height:1.660000
|
|
|
+ height:1.660000
|
|
|
+ end:printStudentInfo
|
|
|
+ Student{name='yangyi', age=18, height=1.66, source=[90, 91, 92], hobby=[学习, null, null]}
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+### 参考资料
|
|
|
+
|
|
|
+[JNI 使用笔记](https://icejoywoo.github.io/2022/08/22/intro-to-jni.html)
|
|
|
+
|
|
|
+[案例代码仓库](https://git.anyi.space/yangyi/JNI_learn)
|