上一篇中,咱們主要學習了Java調用本地方法,並列舉了兩大特殊實例來例證咱們的論據,還沒學習的夥伴必須先去閱讀下,本次的學習是直接在上一篇的基礎上進行了。點擊:Android NDK開發之從Java與C互調中詳解JNI使用(一)html
本篇咱們主要學習如何從C源碼中調用Java代碼,以及使用gradle-experimental來調試原生代碼。java
這裏定義了兩個普通成員變量和一個靜態成員變量。python
就像C不能直接使用Java的引用類型同樣,C也不能直接的訪問Java成員變量,而是經過JNI所封裝的API來調用Java成員。一般會有以下的步驟:android
1:獲取java實例對象的引用
2:經過實例對象獲取java成員變量ID
3:經過變量ID獲取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對象引用。
由第一步咱們獲取到了實例對象的引用,那麼咱們能夠經過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;");
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方法和調用成員變量基本是同樣的,首先咱們如今Java類中定義一個方法,用Toast來顯示信息,如:
上面也說過C調用Java方法和變量步驟基本同樣,下面來看下基本步驟:
1:獲取java實例對象的引用
2:經過實例對象獲取實例方法ID
3:經過方法ID調用實際的Java方法
這一步和C獲取變量所介紹的獲取方式是同樣的,都是經過GetObjectClass或是FindClass函數來獲取的,這裏就再也不贅述,能夠參考上面的實力。
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")
獲取到方法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->File->settings->androidSDK->SDK Tools中下載LLDB:
用項目對gradle-experimental的依賴代替本來項目對gradle的依賴。
① 使用com.android.model.application替代原來的com.android.application
② 把原有的配置存放在model{}中
③ 全部的配置屬性使用等號(=)鏈接
原生C代碼中斷點,而後執行dug運行模式。
ok,接下來就能夠執行你的源代碼了。
好了,今天就講到這裏吧。
請關注微信公衆號。謝謝