上篇文章JNI访问Java对象的成员介绍了如何在JNI层回调Java对象的成员(变量和方法),这篇文章是上篇文章 的姊妹篇,介绍在JNI层如何回调Java类的静态成员(变量和方法)。java
首先呢,仍是须要作一些准备工做,先完成动态注册的代码。android
若是你对动态注册的代码还不熟悉,能够经过JNI函数动态注册和JNI函数动态注册进阶学习。c++
首先在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
就是要实现的方法,在这个方法中将会作三件事情post
Hello.java
类的静态变量mName
的值。Hello.java
类的静态变量mName
的值。Hello.java
的静态方法sayHello
。首先实现获取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类的静态变量的步骤以下spa
在例子中是经过FindClass
函数来获取Class对象的,函数原型以下code
jclass FindClass(JNIEnv *env, const char *name);
复制代码
参数const char * name
能够是全限定的Class名,或者是一个数组类型的签名。对象
String
类,全限定Class名为java.lang.String
,可是因为点号在JNI中有特殊意义,所以使用斜线来代替点号,全限定Class名为java/lang/String
。String[]
,那么参数就要为数组的类型签名[Ljava/lang/String;
。若是还不了解数组的类型的签名是什么,可能参数JNI函数动态注册进阶。
在例子中,是经过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函数动态注册进阶。
根据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类的静态变量的类型的不一样,在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);
}
复制代码
在这个完整实现中,加入了对jclass
和jmethodID
的判空,以及手动删除局部引用的操做。
这篇文章其实和上篇文章很是相似,也很是好理解,只要搞清楚了流程,就能够很是熟练的使用了。
其实还有一个很是有意思的事情,如何访问(获取/设置)Java的数组类型的静态变量?恩,这个问题留到下一篇文章来分析。