转载

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
正文到此结束
Loading...