JNI 编程上手指南之 Native 访问 Java

本文接着介绍如何在 C/C++ 中访问 Java,主要从以下几个方面来讲述:

  • 访问 Java 的成员变量,包括了实例成员和静态成员
  • 访问 Java 的方法,包括了成员方法和静态方法

1. Native 访问 Java 成员变量

我们直接看 Demo:

Java 层:

//定义一个被访问的类
public class TestJavaClass {

    private String mString = "Hello JNI, this is normal string !";

    private static int mStaticInt = 0;
}

//定义两个 native 方法
public native void accessJavaFiled(TestJavaClass testJavaClass);
public native void accessStaticField(TestJavaClass testJavaClass);

c++ 层:

//访问成员变量
extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_accessJavaFiled(JNIEnv *env, jobject thiz,
                                                          jobject test_java_class) {
    jclass clazz;
    jfieldID mString_fieldID;

    //获得 TestJavaClass 的 jclass 对象
    // jclass 类型是一个局部引用
    clazz = env->GetObjectClass(test_java_class);

    if (clazz == NULL) {
        return;
    }

    //获得 mString 的 fieldID
    mString_fieldID = env->GetFieldID(clazz, "mString", "Ljava/lang/String;");
    if (mString_fieldID == NULL) {
        return;
    }

    //获得 mString 的值
    jstring j_string = (jstring) env->GetObjectField(test_java_class, mString_fieldID);
    //GetStringUTFChars 分配了内存,需要使用 ReleaseStringUTFChars 释放
    const char *buf = env->GetStringUTFChars(j_string, NULL);

    //修改 mString 的值
    char *buf_out = "Hello Java, I am JNI!";
    jstring temp = env->NewStringUTF(buf_out);
    env->SetObjectField(test_java_class, mString_fieldID, temp);

    //jfieldID 不是 JNI 引用类型,不用 DeleteLocalRef
    // jfieldID 是一个指针类型,其内存的分配与回收由 JVM 负责,不需要我们去 free
    //free(mString_fieldID);

    //释放内存
    env->ReleaseStringUTFChars(j_string, buf);
    //释放局部引用表
    env->DeleteLocalRef(j_string);
    env->DeleteLocalRef(clazz);

}

//访问静态成员变量
extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_accessStaticField(JNIEnv *env, jobject thiz,
                                                            jobject test_java_class) {
    jclass clazz;
    jfieldID mStaticIntFiledID;

    clazz = env->GetObjectClass(test_java_class);

    if (clazz == NULL) {
        return;
    }

    mStaticIntFiledID = env->GetStaticFieldID(clazz, "mStaticInt", "I");

    //获取静态成员
    jint mInt = env->GetStaticIntField(clazz, mStaticIntFiledID);
    //修改静态成员
    env->SetStaticIntField(clazz, mStaticIntFiledID, 10086);

    env->DeleteLocalRef(clazz);

}

访问一个类成员基本分为三部:

  • 获取到类对应的 jclass 对象(对应于 Java 层的 Class 对象),jclss 是一个局部引用,使用完后记得使用 DeleteLocalRef 以避免局部引用表溢出。
  • 获取到需要访问的类成员的 jfieldID,jfieldID 不是一个 JNI 引用类型,是一个普通指针,指针指向的内存又 JVM 管理,我们无需在使用完后执行 free 清理操作
  • 根据被访问对象的类型,使用 GetxxxField 和 SetxxxField 来获得/设置成员变量的值

2. Native 访问 Java 方法

我们直接看 Demo:

Java 层

//等待被 native 层访问的 java 类
public class TestJavaClass {

    //......
    private void myMethod() {
        Log.i("JNI", "this is java myMethod");
    }

    private static void myStaticMethod() {
        Log.d("JNI", "this is Java myStaticMethod");
    }

}

//本地方法
public native void accessJavaMethod();

public native void accessStaticMethod();

C++ 层:

extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_accessJavaMethod(JNIEnv *env, jobject thiz) {

    //获取 TestJavaClass 对应的 jclass
    jclass clazz = env->FindClass("com/yuandaima/myjnidemo/TestJavaClass");
    if (clazz == NULL) {
        return;
    }

    //构造函数 id
    jmethodID java_construct_method_id = env->GetMethodID(clazz, "<init>", "()V");

    if (java_construct_method_id == NULL) {
        return;
    }

    //创建一个对象
    jobject object_test = env->NewObject(clazz, java_construct_method_id);
    if (object_test == NULL) {
        return;
    }

    //获得 methodid
    jmethodID java_method_id = env->GetMethodID(clazz, "myMethod", "()V");
    if (java_method_id == NULL) {
        return;
    }

    //调用 myMethod 方法
    env->CallVoidMethod(object_test,java_method_id);

    //清理临时引用吧  
    env->DeleteLocalRef(clazz);
    env->DeleteLocalRef(object_test);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_yuandaima_myjnidemo_MainActivity_accessStaticMethod(JNIEnv *env, jobject thiz) {

    jclass clazz = env->FindClass("com/yuandaima/myjnidemo/TestJavaClass");
    if (clazz == NULL) {
        return;
    }

    jmethodID static_method_id = env->GetStaticMethodID(clazz, "myStaticMethod", "()V");
    if(NULL == static_method_id)
    {
        return;
    }

    env->CallStaticVoidMethod(clazz, static_method_id);

    env->DeleteLocalRef(clazz);

}

Native 访问一个 Java 方法基本分为三部:

  • 获取到类对应的 jclass 对象(对应于 Java 层的 Class 对象),jclss 是一个局部引用,使用完后记得使用 DeleteLocalRef 以避免局部引用表溢出。
  • 获取到需要访问的方法的 jmethodID,jmethodID 不是一个 JNI 引用类型,是一个普通指针,指针指向的内存由 JVM 管理,我们无需在使用完后执行 free 清理操作
  • 接着就可以调用 CallxxxMethod/CallStaticxxxMethod 来调用对于的方法,xxx 是方法的返回类型。

参考资料

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/20404,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?