JNI进阶一 (C++调用java属性和方法,javap的使用)

一、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引用):传送门

猜你喜欢

转载自blog.csdn.net/lyh1299259684/article/details/79438802