JNI访问Java类的静态成员

上篇文章JNI访问Java对象的成员介绍了如何在JNI层回调Java对象的成员(变量和方法),这篇文章是上篇文章 的姊妹篇,介绍在JNI层如何回调Java类的静态成员(变量和方法)。

例子

首先呢,还是需要做一些准备工作,先完成动态注册的代码。

如果你对动态注册的代码还不熟悉,可以通过JNI函数动态注册JNI函数动态注册进阶学习。

首先在Java类中加载动态库,然后调用native方法,代码如下

package com.umx.ndkdemo;

public class Person {
    private static String mName = "Nobody";

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

    public native void native_hello();

    public static void sayHello()
    {
        System.out.println(mName + ": Hello World!");
    }
    
    public static void main(String[] args) {
        new Person().native_hello();
    }
}


复制代码

然后在JNI层进行动态注册

#include "jni.h"

static void com_umx_ndkdemo_Person_native_hello(JNIEnv *env, jobject thiz)
{

}

static const JNINativeMethod nativeMethods[] = {
        {"native_hello", "()V", (void *) com_umx_ndkdemo_Person_native_hello}
};

jint JNI_OnLoad(JavaVM * vm, void * reserved) {
    jint jni_version = -1;
    JNIEnv* env = NULL;

    if (vm->GetEnv((void **)&env, JNI_VERSION_1_1) == JNI_OK) {
        jclass clazz_Person = env->FindClass("com/umx/ndkdemo/Person");
        if (env->RegisterNatives(clazz_Person, nativeMethods,
                                 sizeof(nativeMethods) / sizeof(nativeMethods[0])) == JNI_OK) {
            jni_version = JNI_VERSION_1_6;
        }
    }
    return jni_version;
}
复制代码

com_umx_ndkdemo_Person_native_hello就是要实现的方法,在这个方法中将会做三件事情

  • 获取Hello.java类的静态变量mName的值。
  • 重新设置Hello.java类的静态变量mName的值。
  • 调用Hello.java的静态方法sayHello

访问Java类的静态变量

获取静态变量的值

首先实现获取Hello.java静态变量mName的值

#include <android/log.h>
static void com_umx_ndkdemo_Person_native_hello(JNIEnv *env, jobject thiz)
{
    // 获取Class对象
    jclass clazz_Person = env->FindClass("com/umx/ndkdemo/Person");
    // 从Class对象中获取mName字段
    jfieldID fieldID_mName = env->GetStaticFieldID(clazz_Person, "mName", "Ljava/lang/String;");
    // 获取静态变量的值
    jstring mName = (jstring) env->GetStaticObjectField(clazz_Person, fieldID_mName);
    // 打印输出
    __android_log_print(ANDROID_LOG_INFO, "bxll", "name = %\n", mName);
}
复制代码

和Java反射类似,使用JNI获取Java类的静态变量的步骤如下

  • 获取Class对象
  • 获取Class对象的字段名,也就是静态变量名
  • 通过Class对象和字段名,获取静态变量的值

FindClass

在例子中是通过FindClass函数来获取Class对象的,函数原型如下

jclass FindClass(JNIEnv *env, const char *name);
复制代码

参数const char * name可以是全限定的Class名,或者是一个数组类型的签名。

  • 对于Java的String类,全限定Class名为java.lang.String,但是由于点号在JNI中有特殊意义,因此使用斜线来代替点号,全限定Class名为java/lang/String
  • 对于Java的数组类,例如String[],那么参数就要为数组的类型签名[Ljava/lang/String;

如果还不了解数组的类型的签名是什么,可能参数JNI函数动态注册进阶

GetStaticFieldID

在例子中,是通过GetStaticFieldID函数来获取Class对象的静态字段,函数原型如下

jfieldID GetStaticFieldID (JNIEnv *env, 
                            jclass clazz, 
                            const char *name, 
                            const char *sig);
复制代码

参数

  • jclass clazz: Class对象,通过FindClass函数获取。
  • const char *name: Class对象的字段名,也就是Java类的静态变量名。
  • const char *sig: 静态变量的类型签名。

如果还不了解数组的类型的签名是什么,可能参数JNI函数动态注册进阶

GetStatic<type>Field

根据Java类的静态变量的类型的不同,在JNI中有不同的方法来获取静态变量的值,但是基本形式如下

NativeType GetStatic<type>Field(JNIEnv *env, jclass clazz, jfieldID fieldID);
复制代码

这类函数可以是为两类,一类是处理Java的8种基本类型,一类是处理所有的Java引用类型,如下表

方法名 返回值
GetStaticBooleanField jboolean
GetStaticByteField jbyte
GetStaticCharField jchar
GetStaticShortField jshort
GetStaticIntField jint
GetStaticLongField jlong
GetStaticFloatField jfloat
GetStaticDoubleField jdouble
GetStaticObjectField jobject

前8项是处理Java对应的8种基本类型,最后一项是处理其它所有的Java类型。

例如,对于Java类的int类型的静态变量,是用如下函数获取

jint GetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID);
复制代码

而对于Java的String类型的静态变量,是用如下函数获取的

jstring GetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID);
复制代码

设置静态变量的值

现在来实现设置静态变量的值

static void com_umx_ndkdemo_Person_native_hello(JNIEnv *env, jobject thiz)
{
    // 获取Class对象
    jclass clazz_Person = env->FindClass("com/umx/ndkdemo/Person");
    // 获取字段
    jfieldID fieldID_mName = env->GetStaticFieldID(clazz_Person, "mName", "Ljava/lang/String;");
    // 设置字段的值
    jstring name = env->NewStringUTF("David");
    env->SetStaticObjectField(clazz_Person, fieldID_mName, name);

}
复制代码

设置Java类静态变量的值有以下几步

  • 获取Java类的Class对象
  • 获取静态变量的字段
  • 通过Class对象和字符,设置静态变量的值

前两步与已经介绍过,直接说明第三步是如何使用的

SetStatic<type>Field

根据Java类的静态变量的类型的不同,在JNI中有不同的方法来获设置态变量的值,但是基本形式如下

void SetStatic<type>Field(JNIEnv *env, 
                        jclass clazz,
                        jfieldID fieldID, 
                        NativeType value);
复制代码

其中最后一个参数指的是要给Java类的静态变量设置的值,它的类型会根据要设置的静态变量的类型的不同而不同,例如,要给int类型的静态变量设置值,那么这里的NativeType就对应jint

JNI处理Java类型的方式分为基本类型(8种)的引用类型,因此这里对应的JNI方法就有9种

方法名 NativeType
SetStaticBooleanField jboolean
SetStaticByteField jbyte
SetStaticCharField jchar
SetStaticShortField jshort
SetStaticIntField jint
SetStaticLongField jlong
SetStaticFloatField jfloat
SetStaticDoubleField jdouble
SetStaticObjectField jobject

前8项就是用来设置Java的基本类型的,最后一项就是用来处理Java引用类型的。

例如,如果要给Java类的int类型的静态变量设置值,那么就要调用如下函数

void SetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID, jint value);
复制代码

例如,如果要给Java类的String类型的变量设置值,那么就要调用如下的函数

void SetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value)
复制代码

完整实现

static void com_umx_ndkdemo_Person_native_hello(JNIEnv *env, jobject thiz)
{
    jclass clazz_Person = env->FindClass("com/umx/ndkdemo/Person");
    if (clazz_Person == NULL)
    {
        return;
    }

    jfieldID fieldID_mName = env->GetStaticFieldID(clazz_Person, "mName", "Ljava/lang/String;");
    if (fieldID_mName == NULL)
    {
        return;
    }

    jstring mName = (jstring) env->GetStaticObjectField(clazz_Person, fieldID_mName);
    __android_log_print(ANDROID_LOG_INFO, "david", "name = %\n", mName);

    jstring name = env->NewStringUTF("David");
    env->SetStaticObjectField(clazz_Person, fieldID_mName, name);
    
    jmethodID methodID_sayHello = env->GetStaticMethodID(clazz_Person, "sayHello", "()V");
    env->CallStaticVoidMethod(clazz_Person, methodID_sayHello);

    // 删除局部引用(可选)
    env->DeleteLocalRef(name);
    env->DeleteLocalRef(mName);
    env->DeleteLocalRef(clazz_Person);
}
复制代码

在这个完整实现中,加入了对jclassjmethodID的判空,以及手动删除局部引用的操作。

总结

这篇文章其实和上篇文章非常类似,也非常好理解,只要搞清楚了流程,就可以非常熟练的使用了。

其实还有一个非常有意思的事情,如何访问(获取/设置)Java的数组类型的静态变量?恩,这个问题留到下一篇文章来分析。


作者:不惜留恋_
链接:https://juejin.im/post/5d29fd58f265da1b7b31b9e1
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

发布了18 篇原创文章 · 获赞 6 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_37207639/article/details/96141976