(二)JNI 访问 java 属性和方法

版权声明:本文为博主原创文章,未经博主允许不得转载。

本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、JNI 访问 java 属性值

JNI 调用 C 的动态库,在动态库的方法中进行对 java 属性值的修改,这边分为两类,静态属性和非静态属性。

java 代码:

public class JNIMain {

    public native static String getString();

    public native String getString2();

    private static int count = 0;
    public native static void accessStaticField();

    private String key = "test";
    public native void accessField();

    static{
        System.loadLibrary("FirstApplication");
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println(getString());

        JNIMain jniMain = new JNIMain();
        jniMain.getString2();
        System.out.println(jniMain.getString2());

        System.out.println("修改前");
        System.out.println(count);
        System.out.println(jniMain.key);

        accessStaticField();
        jniMain.accessField();

        System.out.println("修改后");
        System.out.println(count);
        System.out.println(jniMain.key);

    }
}

1.修改静态属性值

C 代码:

JNIEXPORT void JNICALL Java_com_xiaoyue_JNIMain_accessStaticField
(JNIEnv *env, jclass jclz) {

    //获取 java 类中静态属性的 id
    jfieldID fid = (*env)->GetStaticFieldID(env, jclz, "count", "I");

    //获取到 java 中的静态属性
    jint count = (*env)->GetStaticIntField(env, jclz, fid);
    count ++;

    //设置 java 中的静态属性
    (*env)->SetStaticIntField(env, jclz, fid, count);
}

2.修改非静态属性值

C 代码:

JNIEXPORT void JNICALL Java_com_xiaoyue_JNIMain_accessField
(JNIEnv *env, jobject jobj) {

    jclass jclz= (*env)->GetObjectClass(env, jobj);

    //获取 java 类中非静态属性的 id
    jfieldID fid = (*env)->GetFieldID(env, jclz, "key", "Ljava/lang/String;");

    //获取到 java 中的非静态属性
    jstring jkey = (*env)->GetObjectField(env, jobj, fid);
    // jni -> c
    char * key = (*env)->GetStringUTFChars(env, jkey, NULL);

    char ch[50] = "change";
    strcat(ch, key);

    // C -> jni
    jstring new_str = (*env)->NewStringUTF(env, ch);
    //设置 java 中的静态属性
    (*env)->SetObjectField(env, jobj, fid, new_str);

    //对旧的字符串进行释放
    (*env)->ReleaseStringChars(env, new_str, key);

}

运行结果:
这里写图片描述

C 语言要对 java 的属性值进行修改,需要用到 JNIEnv,这里有众多的方法可以进行操作。

要操作属性,需要通过 GetStaticFieldID 或 GetFieldID 获取到属性的 id。

    //获取 java 类中静态属性的 id
    jfieldID fid = (*env)->GetStaticFieldID(env, jclz, "count", "I");

    //获取 java 类中非静态属性的 id
    jfieldID fid = (*env)->GetFieldID(env, jclz, "key", "Ljava/lang/String;");

第一个参数:为 JNIEnv
第二个参数:为传入类 jclass ,如果没有的话,要通过 (*env)->GetObjectClass(env, jobj) 进行获取
第三个参数:为要获取的属性名
第四个参数:为对应属性的签名,具体看上一篇

在通过 get 方法获取到属性值,修改完成之后进行 set 方法的操作,有具体分静态、非静态、以及属性类型。

二、JNI 访问 java 方法

与 JNI 访问 java 属性类似,都是通过 JNIEnv 来进行实现。

java 代码:

public class JNIMain {

    //访问 java 非静态方法
    public native void accessMethod();
    //访问 java 静态方法
    public native void accessStaticMethod();

    static{
        System.loadLibrary("FirstApplication");
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        //JNI 基本流程调用
        JNIMain jniMain = new JNIMain();

        //JNI 访问 java 方法
        System.out.println("JNI 访问 java 方法");
        jniMain.accessMethod();
        jniMain.accessStaticMethod();
    }

    int getRandomInt(int max) {
        return new Random().nextInt(max);
    }

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

1.访问静态方法

C 代码:

JNIEXPORT void JNICALL Java_com_xiaoyue_JNIMain_accessStaticMethod
(JNIEnv *env, jobject jobj) {

    jclass jclz= (*env)->GetObjectClass(env, jobj);
    //jmethdId,GETStaticMethodID  方法的名字,方法的签名
    jmethodID jmid = (*env)->GetStaticMethodID(env, jclz, "getRandeomUUId", "()Ljava/lang/String;");

    // 调用静态方法
    jstring uuid = (*env)->CallStaticObjectMethod(env, jclz, jmid); 

    //jstring -> char *
    char * uuid_c = (*env)->GetStringUTFChars(env, uuid, NULL);
    printf("C UUID: %s \n", uuid_c);
}

2.修改非静态方法

C 代码:

//访问 java 非静态方法
JNIEXPORT void JNICALL Java_com_xiaoyue_JNIMain_accessMethod
(JNIEnv *env, jobject jobj) {

    jclass jclz= (*env)->GetObjectClass(env, jobj);

    //获取 java 方法的 id
    jmethodID methodId = (*env)->GetMethodID(env, jclz, "getRandomInt", "(I)I");

    //调用
    jint random = (*env)->CallIntMethod(env, jobj, methodId, 200);
    printf("C random: %d \n", random);
}

运行结果:
这里写图片描述

    //获取 java 方法的 id
    jmethodID methodId = (*env)->GetMethodID(env, jclz, "getRandomInt", "(I)I");

第一个参数:为 JNIEnv
第二个参数:为传入类 jclass ,如果没有的话,要通过 (*env)->GetObjectClass(env, jobj) 进行获取
第三个参数:为要获取的方法名
第四个参数:为对应方法的签名

方法签名的获取:命令行窗口切换到对应类的 .class 文件路径,使用 javap 命令进行查看,熟悉的也可以自己进行获取。

运行结果:
这里写图片描述

三、JNI 访问 java 构造方法

也是通过 JNIEnv 来进行实现。

java 代码:

package com.xiaoyue;

import java.util.Date;
import java.util.Random;
import java.util.UUID;

public class JNIMain {

    //访问 java 构造方法
    public native Date acceessConstructor();

    static{
        System.loadLibrary("FirstApplication");
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        JNIMain jniMain = new JNIMain();

        //JNI 访问 java 构造方法
        System.out.println("JNI 访问 java 构造方法");
        jniMain.acceessConstructor();
    }

C 代码:

JNIEXPORT jobject JNICALL Java_com_xiaoyue_JNIMain_acceessConstructor
(JNIEnv *env, jobject jobj) {

    jclass jclz = (*env)->FindClass(env, "java/util/Date");
    //jmethodid
    jmethodID jmid = (*env)->GetMethodID(env, jclz, "<init>", "()V");

    //调用 newObject 实例化Date 对象,返回值是一个jobjcct
    jobject date_obj = (*env)->NewObject(env, jclz, jmid);

    //得到对应对象的方法,前提是,我们访问了相关对象的构造函数创建了这个对象
    jmethodID time_mid = (*env)->GetMethodID(env, jclz, "getTime", "()J");
    jlong time = (*env)->CallLongMethod(env, date_obj, time_mid);

    printf("time: %lld \n", time);
    return date_obj;
}
    jmethodID jmid = (*env)->GetMethodID(env, jclz, "<init>", "()V");

获取构造函数的方法 id ,传入的方法名都是 “”。

运行结果:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_18983205/article/details/78956260