JNI/NDK開發指南(7):C/C++訪問Java例項變數和靜態變數

發表於2015-03-21

在上一章中我們學習到了如何在原生程式碼中訪問任意Java類中的靜態方法和例項方法,本章我們也通過一個示例來學習Java中的例項變數和靜態變數,在原生程式碼中如何來訪問和修改。靜態變數也稱為類變數(屬性),在所有例項物件中共享同一份資料,可以直接通過【類名.變數名】來訪問。例項變數也稱為成員變數(屬性),每個例項都擁有一份例項變數資料的拷貝,它們之間修改後的資料互不影響。下面看一個例子:

AccessField是程式的入口類,定義了兩個native方法:accessInstanceField和accessStaticField,分別用於演示在原生程式碼中訪問Java類中的例項變數和靜態變數。其中accessInstaceField方法訪問的是類的例項變數,所以該方法需要一個ClassField例項作為形參,用於訪問該物件中的例項變數。

在本例中沒有將例項變數和靜態變數定義在程式入口類中,新建了一個ClassField的類來定義類的屬性,目的是為了加深在C/C++程式碼中可以訪問任意Java類中的屬性。在這個類中定義了一個int型別的例項變數num,和一個java.lang.String型別的靜態變數str。這兩個變數會被原生程式碼訪問和修改。

以上程式碼是程式入口類AccessField.class為native方法生成的原生程式碼函式原型標頭檔案

以上程式碼是對標頭檔案中函式原型的實現。

執行程式,輸出結果如下:

程式碼解析:

一、訪問例項變數

在main方法中,通過呼叫accessInstanceField()方法來呼叫本地函式Java_com_study_jnilearn_AccessField_accessInstanceField,定位到函式32行:

該函式就是用於獲取ClassField物件中num的值。下面是函式的原型:

因為例項變數str是String型別,屬於引用型別。在JNI中獲取引用型別欄位的值,呼叫GetObjectField函式獲取。同樣的,獲取其它型別欄位值的函式還有GetIntField,GetFloatField,GetDoubleField,GetBooleanField等。這些函式有一個共同點,函式引數都是一樣的,只是函式名不同,我們只需學習其中一個函式如何呼叫即可,依次類推,就自然知道其它函式的使用方法。

GetObjectField函式接受3個引數,env是JNI函式表指標,obj是例項變數所屬的物件,fieldID是變數的ID(也稱為屬性描述符或簽名),和上一章中方法描述符是同一個意思。env和obj引數從Java_com_study_jnilearn_AccessField_accessInstanceField函式形參列表中可以得到,那fieldID怎麼獲取呢?瞭解Java反射的童鞋應該知道,在Java中任何一個類的.class位元組碼檔案被載入到記憶體中之後,該class子節碼檔案統一使用Class類來表示該類的一個引用(相當於Java中所有類的基類是Object一樣)。然後就可以從該類的Class引用中動態的獲取類中的任意方法和屬性。注意:Class類在Java SDK繼承體系中是一個獨立的類,沒有繼承自Object。請看下面的例子,通過Java反射機制,動態的獲取一個類的私有例項變數的值:

執行程式後,輸出結果當然是列印出str屬性的值“YangXin”。所以我們在原生程式碼中呼叫JNI函式訪問Java物件中某一個屬性的時候,首先第一步就是要獲取該物件的Class引用,然後在Class中查詢需要訪問的欄位ID,最後呼叫JNI函式的GetXXXField系列函式,獲取欄位(屬性)的值。上例中,首先呼叫GetObjectClass函式獲取ClassField的Class引用:

然後呼叫GetFieldID函式從Class引用中獲取欄位的ID(str是欄位名,Ljava/lang/String;是欄位的型別)

最後呼叫GetObjectField函式,傳入例項物件和欄位ID,獲取屬性的值

呼叫SetXXXField系列函式,可以修改例項屬性的值,最後一個引數為屬性的值。引用型別全部呼叫SetObjectField函式,基本型別呼叫SetIntField、SetDoubleField、SetBooleanField等

二、訪問靜態變數

訪問靜態變數和例項變數不同的是,獲取欄位ID使用GetStaticFieldID,獲取和修改欄位的值使用Get/SetStaticXXXField系列函式,比如上例中獲取和修改靜態變數num:

總結:

1、由於JNI函式是直接操作JVM中的資料結構,不受Java訪問修飾符的限制。即,在原生程式碼中可以呼叫JNI函式可以訪問Java物件中的非public屬性和方法

2、訪問和修改例項變數操作步聚:

1>、呼叫GetObjectClass函式獲取例項物件的Class引用

2>、呼叫GetFieldID函式獲取Class引用中某個例項變數的ID

3>、呼叫GetXXXField函式獲取變數的值,需要傳入例項變數所屬物件和變數ID

4>、呼叫SetXXXField函式修改變數的值,需要傳入例項變數所屬物件、變數ID和變數的值

3、訪問和修改靜態變數操作步聚:、

1>、呼叫FindClass函式獲取類的Class引用

2>、呼叫GetStaticFieldID函式獲取Class引用中某個靜態變數ID

3>、呼叫GetStaticXXXField函式獲取靜態變數的值,需要傳入變數所屬Class的引用和變數ID

4>、呼叫SetStaticXXXField函式設定靜態變數的值,需要傳入變數所屬Class的引用、變數ID和變數的值

示例程式碼下載地址:https://code.csdn.net/xyang81/jnilearn

相關文章