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<char *>(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,"<init>","(Ljava/lang/String;)V");
    jstring  str = env->NewStringUTF("Tony Ya");
    return  env->NewObject(jclazz,jmethodId,str);
}

注意3个点:

  • Student只能通过findClass()获取;
  • 构造方法的方法名是<init>,返回值是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());
}

🎉,谢谢大家的阅读。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容