从零开始使用Cmake进行jni编程教程(三):jni调用java的方法
本文于
1296
天之前发表,文中内容可能已经过时。
本节内容主要是如何在C++层处理,并在java层返回对应的对象和数据。
应用场景
c++函数中有多个返回
c++函数中有多个参数,java端为了简化操作,直接传入一个包含多个参数的对象
c++端需要直接创建java的类,调用方法
具体逻辑 在C++使用的Java的方法:
找到JAVA类(重中之重 )
找到需要的属性
找到需要的方法(注意静态方法和实例方法)
操作方法
编写java对象,用于存储多个参数的值和多个返回
在c++通过反射的方式来获取对应参数的值
调用c++中含有多个参数的方法
调用c++方法结果后,将对应的参数返回
这也是为什么一个so对应有一个jar包的原因,用于确保java方面的类的位置路径不会找错
基本类型 Java类型 | 签名
------------ | -------------
boolean | Z
short | S
float | F
byte | B
int | I
short | S
double | D
short | S
char | C
void | V
short | S
String | java/lang/String
具体实现 1)调用Java中的静态方法 1.新建名叫LogUtils的类
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.minicoder.coder; import android.util.Log; public class LogUtils { private static final String TAG = "LJJ"; public static void D(String message) { Log.d(TAG, message); } }
接下来需要在对应的native方法的c++找到该类,并调用该方法
1 2 3 4 5 6 7 8 // 1.找到类 jclass logclz = env->FindClass("com/minicoder/coder/LogUtils"); // 2.找方法 jmethodID mtd_d = env->GetStaticMethodID(logclz, "D", "(Ljava/lang/String;)V"); jstring data = env->NewStringUTF("从jni中调用的日志"); // 3.调用java层的静态方法 env->CallStaticVoidMethod(logclz, mtd_d, data);
其中方法的签名和类型,就是上面列表中对应的具体类型,其实我本人是不建议大家去记忆的。我是直接这样处理的
1.将原有方法改为native
2.通过之前的javah方法编译后得到的
找方法时传入的三个值,分别对应1.找到的Jclass对象2.需要调用的方法名3.参数签名,随后就可以在C++层调用JAVA层的静态方法了。
2)调用实例方法 与调用静态方法,类似只不过如果该对象没有通过方法传入,则需要在C++里面创建实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 /* * Class: com_minicoder_coder_TestUtils * Method: callInatanceMethod * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_minicoder_coder_TestUtils_callInatanceMethod (JNIEnv *env, jclass clazz, jstring message) { // 1.找到类 jclass logclz = env->FindClass("com/minicoder/coder/LogUtils"); // 2.找构造方法id,用于初始化实例 jmethodID construct = env->GetMethodID(logclz, "<init>", "()V"); jmethodID i = env->GetMethodID(logclz, "I", "(Ljava/lang/String;)V"); // 3.实例化对象 jobject testObj = env->NewObject(logclz, construct, NULL); jstring data = env->NewStringUTF("实例方法打印日志"); // 4.调用实例化对象的方法 env->CallVoidMethod(testObj, i, data); // 5.去掉类引用 // 去掉字符串引用 env->DeleteLocalRef(data); // 去掉找到的类引用 env->DeleteLocalRef(logclz); // 去掉实例化对象 env->DeleteLocalRef(testObj); }
3)调用传入实例的字段和方法 在通过javah编译后,其实所有需要的相关信息都获取了。例如此处方法传入的两个Person对象(因为返回值为bool,还需要一个结果对象)
1 2 3 4 5 6 7 /** * 传入一个人对象,进行修改后再 * @param person 传入的人对象 * @param change 传出改变后的人 * @return 是否改造成功 */ public static native boolean changePerson(Person person,Person change);
对应编译出来的c++的.h文件
1 2 3 4 5 6 7 /* * Class: com_minicoder_coder_TestUtils * Method: changePerson * Signature: (Lcom/minicoder/coder/Person;Lcom/minicoder/coder/Person;)Z */ JNIEXPORT jboolean JNICALL Java_com_minicoder_coder_TestUtils_changePerson (JNIEnv *, jclass, jobject, jobject);
随后新建.cpp文件,老规矩三个步骤
获取类
找到字段
获取值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 /* * Class: com_minicoder_coder_TestUtils * Method: changePerson * Signature: (Lcom/minicoder/coder/Person;Lcom/minicoder/coder/Person;)Z */ JNIEXPORT jboolean JNICALL Java_com_minicoder_coder_TestUtils_changePerson (JNIEnv *env, jclass clazz, jobject person, jobject res) { //1.找到对应的java类 jclass clz = env->FindClass("com/minicoder/coder/Person"); //2. 找到对应的字段 jfieldID jname = env->GetFieldID(clz, "name", "Ljava/lang/String;"); jfieldID jage = env->GetFieldID(clz, "age", "I"); jfieldID jgender = env->GetFieldID(clz, "gender", "I"); // 3.获取数据 jstring pring = (jstring) env->GetObjectField(person, jname); int age = env->GetIntField(person, jage); int gender = env->GetIntField(person, jgender); // 4.数据改造 // 4.1字符串拼接 jstring nameTemp = env->NewStringUTF("红发"); // 4.2 年龄增加 int ageTemp = age + 10; // 4.3 性别改变 int genderTemp = gender + 3; //5.赋值到结果对象 env->SetIntField(res, jage, ageTemp); env->SetIntField(res, jgender, genderTemp); env->SetObjectField(res, jname, nameTemp); // 5.1干掉本地引用 // 去掉类引用 env->DeleteLocalRef(clz); // 去掉字符串引用 env->DeleteLocalRef(data); env->DeleteLocalRef(nameTemp); //6.返回结果值 bool end = false; return end; }