JNI基础(2) - 访问Java变量和对象

1、访问java(私有)变量

逆向步骤:
1)最后通过调用env->Get{type}Field(jobject, fieldId) 得到该变量的值。其中{type} 是变量的类型;如果变量是静态 static 的,则调用的方法是GetStatic{type}Field(jclass, fieldId),注意 static 的话, 是使用 jclass 作为参数;

2)调用env->GetFieldID(jclazz, fieldName, signature)得到该实例域(变量)的 id,即 jfieldID;如果变量是静态 static 的,则调用的方法为 GetStaticFieldID

3)通过env->GetObjectClass(jobject)获取Java 对象的 class 类,返回一个 jclass;

public class JniPractise {
    static {
        System.loadLibrary("ndksample");
    }
    
    // 用public也可以
    private int num = 10;
    public native int nativeNum();
}
jint jin_nativeNum(JNIEnv *env, jobject obj) {
    jclass jclazz = env->GetObjectClass(obj);
    jfieldID jfieldId = env->GetFieldID(jclazz, "num", "I");
    int num = env->GetIntField(obj, jfieldId);
    ++num;
    return num;
}

2、访问java静态变量

 public static String hello= "hello ";
 public static native void nativeHello(String str);

注意:jclass代替了jobject;使用GetStaticFieldID而不是GetFieldID;jsting不能像String一样直接拼接字符串,而是转成const char *,再用strcat()函数拼接2个char *。

void jni_nativeHello(JNIEnv *env, jclass jclazz, jstring param) {
    jfieldID jfieldId = env->GetStaticFieldID(jclazz, "hello", "Ljava/lang/String;");
    jstring hello = (jstring) env->GetStaticObjectField(jclazz, jfieldId);
    char *char_hello = const_cast(env->GetStringUTFChars(hello, JNI_FALSE));
    const char *char_param = env->GetStringUTFChars(param, JNI_FALSE);
    strcat(char_hello, char_param);
    jstring newString = env->NewStringUTF(char_hello);
    env->SetStaticObjectField(jclazz, jfieldId, newString);
}

3、调用java(私有)方法

逆向步骤:
1)通过 JNI 函数env->Call{type}Method(jobject, jmethod, param...)实现调用 Java的方法;若调用的是 static 方法,则使用CallStatic{type}Method(jclass, jmethod, param...),使用的是 jclass

2)通过env->GetMethodID(jclass, methodName, sign)获取到 Java 对象的方法 Id,即 jmethodID,当获取的方法是 static 的时,使用GetStaticMethodID;

3)通过env->GetObjectClass(jobject)获取Java 对象的 class 类,返回一个 jclass;

// 用private也行
public void setNum(int num) {
    this.num = num;
}

public native void accessPublicMethod();

cpp代码:

void jni_accessPublicMethod(JNIEnv *env, jobject obj) {
    jclass jclazz = env->GetObjectClass(obj);
    jmethodID  jmethodId = env->GetMethodID(jclazz,"setNum","(I)V");
    env->CallVoidMethod(obj,jmethodId,13456);
}

4、调用java静态方法

public static String getUUID(){
    return UUID.randomUUID().toString();
}

public static native String accessPublicStaticMethod();

cpp代码;

jstring jni_accessPublicStaticMethod(JNIEnv *env, jclass jclazz) {
    jmethodID jmethodId = env->GetStaticMethodID(jclazz, "getUUID", "()Ljava/lang/String;");
    return (jstring) env->CallStaticObjectMethod(jclazz, jmethodId);
}

5、调用父类的方法

public class Parent {
    public int age(){
        return 50;
    }
}

public class JniPractise extends  Parent{
   @Override
    public int age() {
        return 18;
    }
    
    public native int accessSuperMethod();
}

cpp代码:

jint jni_accessSuperMethod(JNIEnv *env, jobject obj) {
    jclass jclazz = env->FindClass("com/dawn/ndksample/Parent");
    if (!jclazz) {
        return -1;
    }
    jmethodID jmethodId = env->GetMethodID(jclazz, "age", "()I");
    return env->CallNonvirtualIntMethod(obj, jclazz, jmethodId);
}

注意两点不同的地方,

  • 获取的是父类的方法,所以不能通过GetObjectClass获取,需要通过反射 FindClass 获取;
  • 调用父类的方法是 CallNonvirtual{type}Method 函数。Nonvirtual是非虚拟函数

6、jni创建java对象

public class Student {

    public Student(String name) {
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class JniPractise {
        public native Student createStudent();
}
jobject jni_createStudent(JNIEnv *env, jobject obj) {
    jclass jclazz = env->FindClass("com/dawn/ndksample/Student");
    if(jclazz == NULL) {
        return env->NewStringUTF("cannot find class");
    }
    jmethodID  jmethodId = env->GetMethodID(jclazz,"","(Ljava/lang/String;)V");
    jstring  str = env->NewStringUTF("Tony Ya");
    return  env->NewObject(jclazz,jmethodId,str);
}

注意3个点:

  • Student只能通过findClass()获取;
  • 构造方法的方法名是,返回值是V,而不是java中定义的Student。
  • 动态注册时,方式映射表的第二个参数,即方法签名中的返回值类型是:Lcom/dawn/ndksample/Student;

7、jni动态注册

static const JNINativeMethod nativeMethod[] = {
        {"nativeNum",                "()I",                            (void *) jni_nativeNum},
        {"nativeHello",              "(Ljava/lang/String;)V",          (void *) jni_nativeHello},
        {"accessPublicMethod",       "()V",                            (void *) jni_accessPublicMethod},
        {"accessPublicStaticMethod", "()Ljava/lang/String;",           (void *) jni_accessPublicStaticMethod},
        {"accessSuperMethod",        "()I",                            (void *) jni_accessSuperMethod},
        {"createStudent",            "()Lcom/dawn/ndksample/Student;", (void *) jni_createStudent}
};

static int registNativeMethod(JNIEnv *env) {
    int result = -1;
    jclass class_text = env->FindClass("com/dawn/ndksample/JniPractise");
    if (env->RegisterNatives(class_text, nativeMethod,
                             sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
        result = 0;
    }
    return result;
}

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    int result = -1;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
        if (registNativeMethod(env) == JNI_OK) {
            result = JNI_VERSION_1_6;
        }
        return result;
    }
}

8、调试代码

private void test(){
    JniPractise jniPractise = new JniPractise();
    int result =jniPractise.nativeNum();
    Log.d("@@","nativeNum :"+result);
}

private void test2(){
    JniPractise.nativeHello("word");
    Log.d("@@","nativeHello :"+JniPractise.hello);
}

private void test3(){
    JniPractise jniPractise = new JniPractise();
    jniPractise.accessPublicMethod();
    int result =jniPractise.getNum();
    Log.d("@@","accessPublicMethod :"+result);
}

private void test4(){
    Log.d("@@","staticMethod :"+JniPractise.accessPublicStaticMethod());
}

private void test5(){
    JniPractise jniPractise = new JniPractise();
    Log.d("@@","superMethod :"+jniPractise.accessSuperMethod());
}

private void test6(){
    JniPractise jniPractise = new JniPractise();
    Student student = jniPractise.createStudent();
    Log.d("@@","createStudent name :"+student.getName());
}

,谢谢大家的阅读。

你可能感兴趣的:(JNI基础(2) - 访问Java变量和对象)