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

上一篇中,咱們主要學習了Java調用本地方法,並列舉了兩大特殊實例來例證咱們的論據,還沒學習的夥伴必須先去閱讀下,本次的學習是直接在上一篇的基礎上進行了。點擊:Android NDK開發之從Java與C互調中詳解JNI使用(一)html

本篇咱們主要學習如何從C源碼中調用Java代碼,以及使用gradle-experimental來調試原生代碼。java

C 調用 Java 成員變量

  1. 首先咱們如今Java2CJNI類中定義幾個成員變量,以下:

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

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

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

那麼咱們如今分步的講下學習以上的步驟。微信

獲取java實例對象的引用

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

例如:函數

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 簽名映射
boolean Z
byte B
char C
short S
int I
long J
float F
double D
fully-qualified-class Lfully-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,接下來就能夠執行你的源代碼了。

好了,今天就講到這裏吧。

請關注微信公衆號。謝謝

相關文章
相關標籤/搜索