Android NDK開發之C調用Java及原生代碼斷點調試(二)

C 調用 Java 成員變量

  1. 首先我們現在Java2CJNI類中定義幾個成員變量,如下:

這裏定義了兩個普通成員變量和一個靜態成員變量。

就像C不能直接使用Java的引用類型一樣,C也不能直接的訪問Java成員變量,而是通過JNI所封裝的API來調用Java成員。通常會有如下的步驟:

1:獲取java實例對象的引用
2:通過實例對象獲取java成員變量ID
3:通過變量ID獲取java成員變量

那麼我們現在分步的講下學習以上的步驟。

獲取java實例對象的引用

獲取實例對象的引用JNI已爲我們封裝好了方法,我們可以使用GetObjectClass函數來獲取class對象:
jclass (GetObjectClass)(JNIEnv, jobject);

例如:

jclass class = (*env)->GetObjectClass(env, jobj);

jobj對象我們在上一節也講過,這個是Java調用本地方法時,JNI會封裝調用類的一個實例,在這裏就是Java2CJNI類的引用。

另外一種方法也是可以獲取到class對象,就是通過反射機制來獲取對象:
jclass (FindClass)(JNIEnv, const char*);

例如:

jclass class = (*env)->FindClass(env, "com/sanhui/ndkdemo/Java2CJNI");

同上面的方法一樣,都可以獲取Java對象引用。

通過實例對象獲取java成員變量ID

由第一步我們獲取到了實例對象的引用,那麼我們可以通過JNI封裝的方法來獲取實例內的變量ID:

①:獲取普通成員變量ID

通過jfieldID (GetFieldID)(JNIEnv, jclass, const char, const char);可以獲取到一個jfieldID 類型的ID。

GetFieldID函數中第三個參數是Java類中的成員變量的名稱,如果在Java2CJNI類中定義的成員private String codeError = "驗證碼錯誤 !"中codeError 。第四個參數是變量簽名,說白點就是Java類中成員變量的返回類型,如變量codeError 的返回類型是String ,但是String在原生代碼中屬於引用類型,不能直接識別,所以在JNI中有相應的簽名映射,如下表:

Java 類型JNI 簽名映射
booleanZ
byteB
charC
shortS
intI
longJ
floatF
doubleD
fully-qualified-classLfully-qualified-class ;
type[][type
method type( arg-types ) ret-type

上述中基本數據類型的簽名多以大寫類型首字母爲主,但是引用類型是使用“L”+ 類型路徑 + “;”,如String類型則是“Ljava/lang/String;”,數組則是"[" + 類型,如“[I”表示整形數組,如果是Java方法則是“(參數的類型) + 返回值類型”

ok,通過GetFieldID獲取成員變量ID,如:

jfieldID codeErrorID = (*env)->GetFieldID(env,jclazz,"codeError","Ljava/lang/String;");

②:獲取靜態成員變量ID

成員變量分爲普通和靜態變量,那獲取靜態變量該如何呢?JNI也爲我們封裝好了方法:

jfieldID (GetStaticFieldID)(JNIEnv, jclass, const char,
const char
);

通過GetStaticFieldID函數可以獲取到靜態變量ID,參數如①一樣。舉例:

jfieldID loginSuccID = (*env)->GetStaticFieldID(env,jclazz,"loginSucc","Ljava/lang/String;");

通過變量ID獲取java成員變量

ok,獲取完變量ID,我們就可以通過ID來取得變量了,這裏獲取成員變量也是分爲靜態和普通,分別使用:

jobject (GetObjectField)(JNIEnv, jobject, jfieldID);和jobject (GetStaticObjectField)(JNIEnv, jclass, jfieldID);函數。

例如:

jstring jcodeError = (*env)->GetObjectField(env,jobj,codeErrorID);
jstring juserNameError = (*env)->GetObjectField(env,jobj,userNameErrorID);
jstring jloginSucc = (*env)->GetStaticObjectField(env,jclazz,loginSuccID);

ok,到這裏我們就獲取到了Java類中的成員變量,來看下整體代碼:

獲取到Java中的成員變量,然後返回到Java中,然後我們通過Toast給打印出來:

來看下執行結果,是否如我們多料想的一樣:

從上圖中我們看到的執行結果顯然和我們在Java2CJNI中定義的loginSucc成員變量是一樣的,由此可以得出結論就是C成功的調用了Java類中的變量。

注意:由於獲取Java類中的變量需要在原生代碼中調用幾個方法才能獲取到最終的結果,對於性能來說要求的開銷過大,所以建議一般不這樣直接轟C中調用Java的成員變量,如果有需要建議以參數的形式傳遞給原生代碼。

C 調用 Java 方法

C調用Java方法和調用成員變量基本是一樣的,首先我們現在Java類中定義一個方法,用Toast來顯示信息,如:

上面也說過C調用Java方法和變量步驟基本一樣,下面來看下基本步驟:

1:獲取java實例對象的引用
2:通過實例對象獲取實例方法ID
3:通過方法ID調用實際的Java方法

獲取java實例對象的引用

這一步和C獲取變量所介紹的獲取方式是一樣的,都是通過GetObjectClass或是FindClass函數來獲取的,這裏就不再贅述,可以參考上面的實力。

通過實例對象獲取實例方法ID

java中方法分爲兩類,一類是普通的方法,一類是靜態方法。下面來逐一的介紹。

①:獲取普通方法ID:

可以通過jmethodID (GetMethodID)(JNIEnv, jclass, const char, const char);來獲取方法ID,這也是JNI已經封裝好的原生方法,來解釋下這個函數:
GetMethodID函數前兩個參數就不必多介紹了,其中第三個參數是Java類中的方法名稱,對應的是Java2CJNI類中定義的方法:public void showMessage(String message){}中的showMessage。第四個參數是方法簽名,也就是Java類中方法的返回類型,至於什麼是簽名上面已介紹清楚。

獲取方法ID實例:

jmethodID showMessage = (*env)->GetMethodID(env,jclazz,"showMessage","(Ljava/lang/String;)V");

這裏和變量唯一不同的是,方法有可能帶參數,那麼簽名就需要帶上參數簽名和返回值簽名,也就是在()裏的是參數簽名,()外的是返回值簽名,如“(Ljava/lang/String;)V”表示是含有一個String類型的參數和一個void的無返回類型。

②:獲取靜態方法ID:

獲取靜態方法ID會使用JNI的 jmethodID (GetStaticMethodID)(JNIEnv, jclass, const char, const char);函數,它的使用和參數與GetMethodID一樣,並沒有什麼差別。

例如:

jmethodID showMessage = (*env)->GetStaticMethodID(env,jclazz,"showMessage","(Ljava/lang/String;)V")

3:通過方法ID調用實際的Java方法

獲取到方法ID後,我們可以通過JNI提供的回調函數來真正的調用Java方法,這裏也是分爲回調普通方法和靜態方法,由於兩者基本沒什麼差別,我們這裏就只講下普通方法的回到。

C回調Java方法會使用Call< type >Method函數來回調實際的方法,例如,我們調用我們顯示Toast的無返回值方法:

(*env)->CallVoidMethod(env,jobj,showMessage,jloginSucc);

直接的調用CallVoidMethod,它第三個參數傳入的是jmethodID類型的方法ID,由之前獲取到的,第四個參數是要傳遞給Java的參數,這裏接受的是一個String累的字符串。

ok,通過上面的三個步驟,我們已調用了Java的方法了,來看下整體的C代碼實現吧。

好,來看下執行結果:

ok,到這裏C調用Java就講完了,下面講下實用的C原生代碼怎麼斷點調試。

Androidstudio中原生代碼斷點調試

下載C/C++調試器LLDB

在androidstudio->File->settings->androidSDK->SDK Tools中下載LLDB:

在項目目錄下的build.gradle文件添加對gradle-experimental的依賴

用項目對gradle-experimental的依賴代替原本項目對gradle的依賴。

配置工程下的build.gradle

① 使用com.android.model.application替代原來的com.android.application
② 把原有的配置存放在model{}中
③ 所有的配置屬性使用等號(=)連接

DUG運行

原生C代碼中斷點,然後執行dug運行模式。

ok,接下來就可以執行你的源代碼了。


鏈接:http://www.cnblogs.com/guanmanman/p/6840226.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章