上篇文章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的數組類型的靜態變量?恩,這個問題留到下一篇文章來分析。