一、C/C++函数分析:
//获取jclass对象,参数:this的意思,就是native方法所在的类
1.GetObjectClass(jobject)
//获取普通属性id,第一个参数:类对象, 第二个参数:属性名,第三个参数:属性签名(不知道的同学点击这里)
2.GetFieldID(jclass clazz, const char* name, const char* sig)
//设置int属性的值, 第一个参数:this的意思, 第二个参数:获取属性id, 第三个参数:要设置的值
3.SetIntField(jobject obj, jfieldID fieldID, jint value)
当然这里就只列举SetIntField函数了,同理还有很多,比如:SetCharField,SetFloatField,SetObjectField ......。有Set函数肯定也会有Get函数,与之对应的就是GetIntField(jobject obj, jfieldID fieldID),这个函数是获取指定属性的值,参数含义同SetIntField函数
//获取静态属性Id, 第一个参数:类对象, 第二个参数: 属性名, 第三个参数: 属性签名
4.GetStaticFieldID(jclass clazz, const char* name, const char* sig)
//设置静态属性的值, 第一个参数: 类对象, 第二个参数: 属性id, 第三个参数: 要设置的值
5.SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
//获取函数id, 第一参数:类对象, 第二个参数:函数名, 第三个参数: 函数签名(不知道的同学点击这里)
6.GetMethodID(jclass clazz, const char* name, const char* sig)
//调用java中的无返回值函数, 第一个参数: this的意思, 第二个参数: 函数id, 第三个参数:需要传入的实参
7.CallVoidMethod(jobject obj, jmethodID methodID, ...)
//获取静态函数id, 第一个参数: 类对象, 第二个参数: 函数名, 第三个参数: 函数签名
8.GetStaticMethodID(jclass clazz, const char* name, const char* sig)
//调用java中无返回值的静态函数, 第一个参数: 类对象, 第二个参数: 函数id, 第三个参数: 需要传入的实参
9.CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
//生成一个jstring类型的方法转换,该方法会返回一个jstring类型
10.NewStringUTF(const char* bytes)
//调用java中的对象类型(String类型被认为对象类型),第一参数:this的意思, 第二个参数:函数Id, 第三个参数:需要传入的实参
11.CallObjectMethod(jobject, jmethodID, ...);
//获取类中的对象属性,第一个参数:this的意思 , 第二个参数:属性id
12.GetObjectField(jobject obj, jfieldID fieldID)
//根据子类的类对象,获取父类的类对象, 第一参数:子类类对象
13.GetSuperclass(jclass clazz)
//调用java中父类的方法,第一个参数:子类的对象, 第二个参数:父类的类对象, 第三个参数:父类的函数id, 第四个参数:需要传入的实参
14.CallNonvirtualVoidMethod(jobject obj, jclass clazz, jmethodID methodID, ...)
了解完这些函数之后我们来看具体使用:
Java_com_liyahong_jni_1field_1method_MainActivity_stringFromJNI( JNIEnv *env, jobject jobj) { //修改普通成员变量 jclass jclazz = env->GetObjectClass(jobj); jfieldID jfieilId = env->GetFieldID(jclazz, "anInt", "I"); jint anInt = 35; env->SetIntField(jobj, jfieilId, anInt); //修改静态成员变量 jfieldID jstaticFieldId = env->GetStaticFieldID(jclazz, "anIntS", "I"); env->SetStaticIntField(jclazz, jstaticFieldId, 55); //通过jni调用普通java方法 jmethodID jmethodId = env->GetMethodID(jclazz, "test", "()V"); env->CallVoidMethod(jobj, jmethodId); //通过jni调用静态java方法 jmethodID jstaticMethodId = env->GetStaticMethodID(jclazz, "staticTest", "(I)V"); env->CallStaticVoidMethod(jclazz, jstaticMethodId, 55); //通过jni调用java中带参方法 jmethodID methodId = env->GetMethodID(jclazz, "test", "(ILjava/lang/String;)Ljava/lang/String;"); jstring str = env->NewStringUTF("小丽"); env->CallObjectMethod(jobj, methodId, 18, str); //通过jni调用其他类中的方法 jfieldID sonJfieldId = env->GetFieldID(jclazz, "son", "Lcom/liyahong/jni_field_method/Son;"); jobject sonJobj = env->GetObjectField(jobj, sonJfieldId); jclass sonClazz = env->GetObjectClass(sonJobj); jmethodID sonMethodId = env->GetMethodID(sonClazz, "ride", "()V"); env->CallVoidMethod(sonJobj, sonMethodId); //通过jni调用父类中的方法 jclass jfatherClazz = env->GetSuperclass(sonClazz); jmethodID jfatherMethodId = env->GetMethodID(jfatherClazz, "ride", "()V"); env->CallNonvirtualVoidMethod(sonJobj, jfatherClazz, jfatherMethodId); std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
二、当使用jni调用java中的构造方法的时候:
其中的方法名需要写为<init>,例如:
public Student(){ ...... }
这时候不应该写Student,而是应该写为:<init>
方便大家理解这里特意写了一个例子:
//通过jni实例父类的构造方法 jmethodID fatherMethodId = env->GetMethodID(jfatherClazz, "<init>", "()V"); jobject fatherJobj = env->NewObject(jfatherClazz, fatherMethodId); //获取父类中的属性fatherName jfieldID fatherFieldId = env->GetFieldID(jfatherClazz, "fatherName", "Ljava/lang/String;"); jstring fatherName = env->NewStringUTF("My name is 张三"); //修改fatherName的值为My name is 张三 env->SetObjectField(fatherJobj, fatherFieldId, fatherName); jmethodID fatherGetNameId = env->GetMethodID(jfatherClazz, "getFatherName", "()Ljava/lang/String;"); //调用父类中的getFatherName方法,获取属性值 fatherName = (jstring) env->CallObjectMethod(fatherJobj, fatherGetNameId); char* resultName = jstringToChar(env, fatherName); LOGE("%s", resultName);
①这里涉及到新函数NewObject,其实这个函数很简单就是一个实例对象的函数,我们知道java中的构造函数都是用来实例对象的,所以顾名思义的,我们要通过jni来调用java中的构造函数,就应该调用NewObject这个函数,这个函数会返回一个jobject对象,这个对象就是我们实例好的java对象。
②这里还涉及到了jstringToChar函数,这个函数看名字想必大家都应该知道什么意思了,这个函数其实就是一个jstring 和 char*的一个类型转换,原因都不细说了,直接上代码:
#include <string.h> #include <stdlib.h> // 由于jvm和c++对中文的编码不一样,因此需要转码。 utf8/16转换成gb2312 char* jstringToChar(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("UTF-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*) malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; }
三、javap的使用:
1.预热工作,首先我们要进入classes目录下使用命令:
cd app/build/intermediates/classes/debug/包名
例如:
cd app/build/intermediates/classes/debug/com/xxx/jni_field_method
下面开始命令:
//获取MainAtivity下的所有属性和方法 javap -p MainActivity
//获取MainAtivity的属性和方法的签名 javap -s -p MainActivity
JNI进阶二(C++调用java数组 和 JNI引用):传送门