版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tongsiw/article/details/83514887
此篇幅主要讲解java调用jni的方法和jni调用java
一、Jni调用Java代码
jni可以调用java中的方法和java中的成员变量,因此JNIEnv定义了一系列的方法来帮助我们调用java的方法和成员变量。
JNI类型 | C/C++类型 | 所表示的含义 |
---|---|---|
jclass | GetObjectClass(jobject obj) | 获取对象obj的jclass |
jclass | FindClass(const char* name) | 获取指定类的class,name是所需要的全类名。 如:FindClass(“java/util/Date”) |
jobject | NewObject(jclass clazz, jmethodID methodID, …) | 获取指定类的对象。 如:要获取Date的对象:NewObject(FindClass(“java/util/Date”),“构造方法”) |
jfieldID | GetFieldID(jclass clazz, const char* name, const char* sig) | 获取java类中成员变量的id。 第一个参数是对象的jclass; 第二个参数是java类中的成员变量名; 第三个参数是java类中成员变量名的签名 |
jobject | GetObjectField(jobject obj, jfieldID fieldID) | 获取jaa类中成员变量的值。 第一个参数是java类的对象 第二个参数就是上述的java类中成员变量的id |
void | SetObjectField(jobject obj, jfieldID fieldID, jobject value) | 给java类的成员变量赋值。 第一个参数:类的对象; 第二个参数:成员变量的id; 第三个参数:给java成员变量所赋的值 |
jmethodID | GetMethodID(jclass clazz, const char* name, const char* sig) | 获取java类的方法id 第一个参数:对象的jclass; 第二个参数:方法名; 第三个参数:java方法签名 |
jobject | env->CallObjectMethod(jobject jobject, jmethodID methodID…) | 调用java类的方法 |
以上就是jni调用java类的大部分方法,如果是静态的成员变量和静态方法,可以使用***GetStaticMethodID、CallStaticObjectMethod等***。就是在上述表格中的相应方法中加个static。
上述中有一个重要的点就是:构造方法的方法id获取,GetMethodID第二个参数传***"< init >"***,这个是固定写法,不能变
上述中还有一个重要的点就是:方法签名(GetFieldID,GetMethodID中的需要的sig参数),这玩意需要记住的,如果记不住,可以通过 javap -s -p 命令去获取,关于javap命令不多说了,下面给一个实例命令行:
javap -s -p E:\1_Study_Space\6_JNI\2_JNI\app\build\intermediates\classes\debug\tsw\demo\a2_jni\Student.class
下面是jni调用java类方法的实例代码
//jni调用java方法
public native void jniCallJava();
public class Student {
public String name;
public String sex;
public Student() {
}
public Student(String name, String sex) {
this.name = name;
this.sex = sex;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
extern "C"
JNIEXPORT void JNICALL
Java_tsw_demo_a2_1jni_TestJni_jniCallJava(JNIEnv *env, jobject instance) {
//------------------通过无参构造获取Student类的对象,同时调用setName方法给对象赋值------------------
jobject jobj_student;
jmethodID jmid_tostring;
//1、获取java类Student的jclass
jclass jcla_student = env->FindClass("tsw/demo/a2_jni/Student");
//2、获取Student的无参构造方法id。构造方法第二个参数,固定传 <init>,不能变;第三个参数是方法签名
jmethodID jmid_student = env->GetMethodID(jcla_student, "<init>", "()V");
//3、获取student对象
jobj_student = env->NewObject(jcla_student, jmid_student);
//4、获取Student类setName方法id。第二个参数是方法名;第三个参数是方法签名
jmethodID jmid_setname = env->GetMethodID(jcla_student, "setName", "(Ljava/lang/String;)V");
//5、调用Student类setName方法。因为Student的setName是void类型,所以用CallVoidMethod。
//java类的方法返回什么类型,就用相应类型的Call<Type>Method方法。Call<Type>Method表示CallVoidMethod、CallLongMethod、CallObjectMethod等。
env->CallVoidMethod(jobj_student, jmid_setname, env->NewStringUTF("zhangsan"));
//6、调用Student的toString方法
jmid_tostring = env->GetMethodID(jcla_student, "toString", "()Ljava/lang/String;");
jstring j_tostring = (jstring) env->CallObjectMethod(jobj_student, jmid_tostring);
LOGE("setName方法给对象赋值: %s", env->GetStringUTFChars(j_tostring, JNI_FALSE));
//--------------通过有参构造,给Student对象赋值----------------------
//获取Student类的有参构造方法id
jmethodID jmid_student_hvae = env->GetMethodID(jcla_student, "<init>","(Ljava/lang/String;Ljava/lang/String;)V");
//通过有参构造方法创建Student对象,并同时赋值
jobj_student = env->NewObject(jcla_student, jmid_student_hvae, env->NewStringUTF("lisi"),env->NewStringUTF("women"));
jmid_tostring = env->GetMethodID(jcla_student, "toString", "()Ljava/lang/String;");
jstring j_str = (jstring) env->CallObjectMethod(jobj_student, jmid_tostring);
LOGE("有参构造给对象赋值: %s", env->GetStringUTFChars(j_str, JNI_FALSE));
//--------------修改Student对象成员变量name的值(上面student对象的name=lisi,将lisi修改成wangwu)----------------------
jfieldID jfid_name = env->GetFieldID(jcla_student, "name", "Ljava/lang/String;");
env->SetObjectField(jobj_student,jfid_name,env->NewStringUTF("wangwu"));
jmid_tostring = env->GetMethodID(jcla_student, "toString", "()Ljava/lang/String;");
jstring j_fieldstr = (jstring) env->CallObjectMethod(jobj_student, jmid_tostring);
LOGE("修改对象成员变量的值: %s", env->GetStringUTFChars(j_fieldstr, JNI_FALSE));
///最后带有Nonvirtual的方法,如:CallNonvirtualObjectMethod调用的是父类的方法,而不是子类的,这个要注意
}
二、Java调用jni中代码
1、Java调用jni方法,并传一个基本类型的参数
java代码
public native int operatInt(int num);
public static native int operatStaticInt(int num);
extern "C"
JNIEXPORT jint JNICALL
Java_tsw_demo_a2_1jni_TestJni_operatInt(JNIEnv *env, jobject instance, jint num) {
num = num + 1;
return num;
}
extern "C"
JNIEXPORT jint JNICALL
Java_tsw_demo_a2_1jni_TestJni_operatStaticInt(JNIEnv *env, jclass type, jint num) {
num = num + 1;
return num;
}
由上述代码可以发现:
- 当java类的native方法是非静态的时候,jni方法的第二个参数是jobject类型的。
- 当java类的native方法是静态的时候,jni方法的第二个参数是jclass类型的。
- 第三个参数就是java传过来的int类型的参数,java的int类型与jni的jint类型相对应。jni返回的也应当是jint类型
2、Java调用jni方法,并传一个数组类型的参数
首先说下JNI为我们定义了哪些数组类型:如下
jbooleanArray、jbyteArray、jcharArray、jshortArray、jintArray、jlongArray、jfloatArray、jdoubleArray、jobjectArray
上面是JNI定义的类型,并不是C/C++的数组,所以需要将上述的数组类型转换成C/C++类型的数组。而JNIEnv定义了一系列的方法来转换:如下
以jobject为例,每个类型的数组JNIEnv中都有定义,就不赘述了。
JNI类型 | C/C++类型 | 所表示的含义 |
---|---|---|
jsize | GetArrayLength(jarray array) | 获得数组的长度 |
jobject | GetObjectArrayElement(jobjectArray array, jsizeindex) | 获取数组的指针 |
jobjectArray | NewObjectArray(jsize length, jclass elementClass,jobject initialElement) | 创建一个指定大小的数组 |
. | SetObjectArrayElement(jobjectArray array, jsize index, jobject value) | 设置数组中的元素 |
具体的请看代码中的注释
(1)、操作int类型的数组
//传递一个int类型数组并修改数组里的值,同时创建一个长度为num的数组返回
public native int[] operatIntArray(int[] nums,int num);
extern "C"
JNIEXPORT jintArray JNICALL
//传递一个数组,修改数组里的值,同时创建一个长度为num的数组返回
Java_tsw_demo_a2_1jni_TestJni_operatIntArray(JNIEnv *env, jobject instance, jintArray nums_,jint num) {
//---------下面就是修改数组的值--------------------------------------------------------------------------------
//1、获取数组的指针
// GetIntArrayElements两个参数,第一个是jintArray没啥说的,第二个是 jboolean* 表示是否复制,一般NULL和JNI_FALSE是一样的表示不复制,JNI_TRUE表示复制
jint *nums = env->GetIntArrayElements(nums_, NULL);
//2、获取数组的长度
jsize length = env->GetArrayLength(nums_);
//3、修改数组的值
for (int i = 0; i < length; ++i) {
nums[i] = i;
}
//4、释放资源。这个一定得写,感觉不仅仅是释放资源,应该还有一个java和c同步的操作,
env->ReleaseIntArrayElements(nums_, nums, 0);
for (int i = 0; i < length; ++i) {
LOGE("数组的值 :%d", nums[i])
}
//---------下面是创建一个新的数组----------------------------------------------------------------------------
//1、创建一个长度为num的jint空数组
jintArray jarr = env->NewIntArray(num);
//2、获取数组的指针
jint *carr = env->GetIntArrayElements(jarr, JNI_FALSE);
//3、给数组赋值
for (int i = 0; i < num; i++) {
carr[i] = i * 2;
}
//4、释放资源。
env->ReleaseIntArrayElements(jarr, carr, 0);
return jarr;
}
(2)、操作String类型的数组
//传递一个String类型数组并修改数组里的值,同时创建一个长度为num的数组返回
public native String[] operatStringArray(String[] nums,int num);
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_tsw_demo_a2_1jni_TestJni_operatStringArray(JNIEnv *env, jobject instance, jobjectArray nums, jint num) {
//---------下面就是修改数组的值--------------------------------------------------------------------------------
//1、获取数组的长度
jsize length = env->GetArrayLength(nums);
//2、这里要注意:获取Object类型的数组和基本类型的数组中的元素方式是不一样的。
// 基本类型的数组首先是通过GetIntArrayElements方法获取数组指针,然后通过遍历的方式拿到数组中的元素。
// Object类型的数组是通过GetObjectArrayElement(jobjectArray array, jsize index)方法拿到数组中的每一个元素。
// Object类型数组的赋值只能通过SetObjectArrayElement(jobjectArray array, jsize index, jobject value)方法;
for (int i = 0; i < length; i++) {
//获取Object类型数组中的元素。String是Object类型数组
jstring jstr = (jstring) env->GetObjectArrayElement(nums, i);
//下面是c字符串拼接操作。
char *cstr = (char *) env->GetStringUTFChars(jstr, JNI_FALSE);
char *addstr = "new";
strcat(cstr, addstr);
//将拼接之后的新的字符串重新转换成jstring
jstring jnew_str = env->NewStringUTF(cstr);
//将转换之后jstring,也就是上面的jnew_str重新赋值给Object数组
env->SetObjectArrayElement(nums,i,jnew_str);
}
//下面是打印到logcat日志
for (int i = 0; i < length; i++) {
jstring jstr = (jstring) env->GetObjectArrayElement(nums, i);
char *cstr = (char *) env->GetStringUTFChars(jstr, JNI_FALSE);
LOGE("数组的值 :%c", cstr[0]);
LOGE("数组的值 :%c", cstr[1]);
LOGE("数组的值 :%c", cstr[2]);
LOGE("数组的值 :%c", cstr[3]);
}
//---------下面就是创建一个Object的数组--------------------------------------------------------------------
//获取String所属类,一般为java/lang/String就可以了。其实这里应该能想到如果是一个别java类,这里应该写这个java类的全类名。
jclass strClass = (env)->FindClass("java/lang/String");
//创建一个长度为num的String数组
jobjectArray jStrArray = env->NewObjectArray(num, strClass, JNI_FALSE);
char* tempArr[] = { " I ", " create", " in ", " JNI" };
for(int i = 0;i < num ;i++ ){
//将c字符串转换成jstring
jstring jstr_create = env->NewStringUTF(tempArr[i]);
//将jstring设置到String数组中
env->SetObjectArrayElement(jStrArray,i,jstr_create);
}
return jStrArray;//将新创建好的String数组返回给java
}
(3)、操作自定义Java类的的数组
public class Student {
public String name;
public String sex;
}
//获取一个自定义java对象
public native Student getStudent();
//获取一个自定义长度的java对象数组
public native Student[] getStudents(int num);
extern "C"
JNIEXPORT jobject JNICALL
Java_tsw_demo_a2_1jni_TestJni_getStudent(JNIEnv *env, jobject instance) {
//其实这个内容前面基本已经说过,要想通过jni生成一个java类对象,其实最主要的一步就是获取那个java类的对象
//1、获取到java类Student的class
jclass jclsStu = env->FindClass("tsw/demo/a2_jni/Student");
//2、第二部获取Student的构造方法,第二个参数固定传<init>,第三个方法是签名,无参构造方法的签名就是 ()V
jmethodID jstu_constructor_mid = env->GetMethodID(jclsStu, "<init>", "()V");
//3、获取Student的对象,后面给对象赋值就完成了java类对象的创建
jobject jobjStu = env->NewObject(jclsStu, jstu_constructor_mid);
//4、获取Student的成员变量name的jfieldID
//GetFieldID方法的第三个参数是Student类成员变量name的签名,前面已经说过,这里就不赘述了。
jfieldID jname_fid = env->GetFieldID(jclsStu, "name", "Ljava/lang/String;");
//5、给Student的成员变量赋值
jstring jstr_name = env->NewStringUTF("zhangsan");
env->SetObjectField(jobjStu, jname_fid, jstr_name);
//如上4、5步骤
jfieldID jsex_fid = env->GetFieldID(jclsStu, "sex", "Ljava/lang/String;");
jstring jstr_sex = env->NewStringUTF("man");
env->SetObjectField(jobjStu, jsex_fid, jstr_sex);
return jobjStu;
}
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_tsw_demo_a2_1jni_TestJni_getStudents(JNIEnv *env, jobject instance, jint num) {
//1、获取jclass
jclass jclaStu = env->FindClass("tsw/demo/a2_jni/Student");
//2、获取Student的构造方法
jmethodID jstu_constructor_mid = env->GetMethodID(jclaStu, "<init>", "()V");
//3、获取Student成员变量的fieldId
jfieldID jfid_name = env->GetFieldID(jclaStu, "name", "Ljava/lang/String;");
jfieldID jfid_sex = env->GetFieldID(jclaStu, "sex", "Ljava/lang/String;");
char* names[] = {"zhangsna","lisi","wangwu"};
char* sexs[] = {"man","woman","man"};
//4、创建一个空的object数组
jobjectArray jarr_stu = env->NewObjectArray(num, jclaStu, JNI_FALSE);
for (int i = 0; i < num; i++) {
//创建Student对象
jobject jobjStu = env->NewObject(jclaStu, jstu_constructor_mid);
//给对象赋值
env->SetObjectField(jobjStu,jfid_name,env->NewStringUTF(names[i]));
env->SetObjectField(jobjStu,jfid_sex,env->NewStringUTF(sexs[i]));
//给数组赋值
env->SetObjectArrayElement(jarr_stu,i,jobjStu);
}
return jarr_stu;
}
3、Java调用jni方法,并获取一个List类型的集合
public class Student {
public String name;
public String sex;
}
//获取一个集合
public native List<Student> getListStudent(int num);
extern "C"
JNIEXPORT jobject JNICALL
Java_tsw_demo_a2_1jni_TestJni_getListStudent(JNIEnv *env, jobject instance, jint num) {
//1、获取ArrayList的class
jclass jcls_arraylist = env->FindClass("java/util/ArrayList");
//2、获取ArrayList的无参构造方法id,以便用来获取ArrayList的对象
jmethodID jarraylist_constructor_mid = env->GetMethodID(jcls_arraylist, "<init>", "()V");
//3、获取ArrayList的对象
jobject jobj_arraylist = env->NewObject(jcls_arraylist, jarraylist_constructor_mid);
//4、获取Arraylist的add方法
jmethodID jmid_add = env->GetMethodID(jcls_arraylist, "add", "(Ljava/lang/Object;)Z");
//接下来的步骤就是创建Student对象,然后给对象赋值并添加到Arraylist集合中去,步骤上面有,就不赘述了
jclass jclaStu = env->FindClass("tsw/demo/a2_jni/Student");
jmethodID jstu_constructor_mid = env->GetMethodID(jclaStu, "<init>", "()V");
jfieldID jfid_name = env->GetFieldID(jclaStu, "name", "Ljava/lang/String;");
jfieldID jfid_sex = env->GetFieldID(jclaStu, "sex", "Ljava/lang/String;");
char* names[] = {"zhangsna","lisi","wangwu"};
char* sexs[] = {"man","woman","man"};
for (int i = 0; i < num; ++i) {
jobject jobjStu = env->NewObject(jclaStu, jstu_constructor_mid);
env->SetObjectField(jobjStu,jfid_name,env->NewStringUTF(names[i]));
env->SetObjectField(jobjStu,jfid_sex,env->NewStringUTF(sexs[i]));
//Student对象创建完成并赋值了,接下来就是调用Arraylist的add方法,将Student对象放到Arraylist集合中区
//因为Arraylist的add方法会返回一个boolean值,所以使用CallBooleanMethod
env->CallBooleanMethod(jobj_arraylist,jmid_add,jobjStu);
}
return jobj_arraylist;
}
可以新建一个项目,把下载好的java文件放到项目目录下,其中的c++代码拷贝到native-lib.cpp文件中,然后修改jni中的方法名即可