標籤:android jni getobjectclass java對象 jni方法
前面兩篇文章簡單介紹了JNI層跟Java層的一些對應關係,包括方法名,資料類型和方法名稱等,相信在理論層面,能夠很好地協助我們去瞭解JNI在Native本地開發中的作用,對JNI的一些概念也有了一個初步的認識,由於表達能力或者理解還是有限,有些地方講得不是很清楚,如果各位朋友有覺得雲裡霧裡,歡迎大家留言一起學習。
概念上的理解有助於我們更好地認識JNI,而一些實際點的例子則能夠更好地幫我們從代碼上去掌握並應用JNI。
在第一篇文章,我們是從一個小例子來入門學習的,在其中,我們通過JNI層函數返回了一字串,如下:
JNIEXPORT jstring JNICALL Java_com_lms_jni_HwDemo_printHello (JNIEnv *e, jobject j){return (**e).NewStringUTF(e,"Hello from T");}
這是一種最簡單的情況,但更多時候,我們需要在JNI層獲得Java對象,對其進行操作,最後將結果返回到Java端,所以這個時候我們就要利用到JNI函數定義的第二個參數 jobject了。
上一篇文章,我們說過,JNIEnv *和jobejct參數都是JNI層方法添加的參數,關於JNIEnv*我們已經在前面的文章簡單介紹過,而jobject參數呢,則我們這一篇文章要操作到的參數了。
對於本地方法,即在Java中定義的native方法,有靜態(static)和非靜態方法,而我們知道,靜態方法它是屬於這個類的方法,對象不能操作它,而非靜態方法則剛好相反,所以在JNI層的方法參數中:
1)對於靜態(static)方法,jobject參數表示的是對應Java類的引用。
2)對於非靜態方法,jobject表示的是對應Java對象的引用。
這一點,應該不難理解。
接下來,我們通過一個小Demo來學習怎麼在JNI層操作Java端的對象,並且改變其中的值。
首先,我們在Java類中定外一個static的變數testval,還有一個方法changeTestVal(),用來改變testval的值,如下:
public class ParamTransferTest {public static int testval = 1;public native void changeTestVal();...}
當然,首先,第一步,我們要在C中實現其對應的函數了,如下:
JNIEXPORT void JNICALL Java_com_lms_jni_ParamTransferTest_changeTestVal (JNIEnv * env, jobject obj){jclass clazz = (*env)->GetObjectClass(env,obj);jint val = (*env)->GetStaticIntField(env, clazz,(*env)->GetStaticFieldID(env, clazz,"testval","I"));LOGI("before change testval = %d", val);val = val + 1;LOGI("after change testval = %d", val);(*env)->SetStaticIntField(env, clazz,(*env)->GetStaticFieldID(env, clazz,"testval","I"),val);}
我們在對應的c檔案中來實現這個native方法,因為實現的是非靜態方法,所以jobject傳過來的就是對該對象的引用,所以我們需要通過GetObjectClass方法來獲得該對象對應的類。
一般在JNI中,我們會利用FindClass和GetObjectClass兩個方法來獲得對應的類,並放到jclass類型的變數中去,不過在這裡注意一點,用C實現和用C++實現的代碼對於JNI的調用方法是不一樣的。
在前面文章中說過,C++對JNINativeInterface定義的方法進行了一層封裝,所以其參數不再需要傳遞env進去,而C則是需要的,比如上面*env調用的方法,如果是用C++實現的話,那麼是不再需要傳遞env參數進去的,即 GetObjectClass(jobject)就可以了。
1)利用GetObjectClass方法獲得jclass。
2)調用GetStaticIntFieldID獲得對應class對應的變數,即jclass中的類型為I(即int)的靜態(static)變數 testval。
3)調用GetStaticIntField獲得對應變數的值 val。
4)改變val 的值,在這裡,我們進行加1操作。
5)調用SetStaticIntField來設定對應變數的值。
所以,在這裡我們發現,Env其實提供了很多的方法,對於訪問物件變數值的,分為靜態非靜態,基本上就是Get<Type>Field和GetStatic<Type>Field,
而相應的,也有Set<Type>Field和SetStatic<Type>Field方法。
而如果調用方法呢,就是利用Call<Type>Method和CallStatic<Type>Method方法了,這些大家可以自己去jni.h檔案中自己看一下,就大概知道怎麼做了。
JNI層這邊實現好了之後,我們利用ndk-build工具重建一個so庫,載入到Android中,在Activity中直接調用方法,如下:
TextView tvChangeTestVal = (TextView)findViewById(R.id.tvChangeTestVal);ParamTransferTest ptt = new ParamTransferTest();ptt.changeTestVal();tvChangeTestVal.setText("" + ptt.testval);
我們調用方法之後,在螢幕上將調用方法後的值,顯示出來,結果應該是1+1=2,對吧,看下面結果:
的確如我們所想像的,它的值已經變化成2了,對吧,說明我們的確是通過native方法在JNI層改變了其值。
我們剛才也在JNI中添加了log,來展示其改變前後的值,如下:
通過這樣一個簡單的小例子,相信大家應該就知道了怎麼樣在JNI層來操作Java端的資料了,對吧。
結束!