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

来源:http://www.cnblogs.com/guanmanman/archive/2017/05/11/6840226.html
-Advertisement-
Play Games

本篇我們主要學習如何從C源碼中調用Java代碼:調用Java類中的成員變數,成員方法,同時也講介紹使用gradle-experimental來調試原生代碼。 ...


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

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

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 簽名映射
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,接下來就可以執行你的源代碼了。

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

請關註微信公眾號。謝謝


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • node上傳圖片第一種方式 1,首先引入模塊 "connect-multiparty": "~1.2.5", 在package.json中添加 "connect-multiparty": "~1.2.5", 然後在命令中切換到項目目錄,使用npm命令:npm installl; 基本配置完成 ind ...
  • Array.from() 用於將兩類對象轉為真正的數組,類似數組對象和可遍歷對象(包括數據結構Set和Map)轉化為數組 格式:Array.from(arrayLike[, mapFn[, thisArg]]) // 字元串 Array.from('foo'); // ["f", "o", "o"] ...
  • 我們就把這個問題叫做圖片查看器吧,它的主要功能有: 1、雙擊縮放圖片。 2、 雙指縮放圖片。 3、單指拖拽圖片。 為此這個圖片查看器需要考慮以下的技術點: 一、雙擊縮放圖片: 1、如果圖片高度比屏幕的高度小得多,那麼就將圖片放大到高度與屏幕高度相等,否則就放大一個特定的倍數。 2、如何判斷是否到達這 ...
  • 1 方法:public boolean moveTaskToBack(boolean nonRoot) activity里有這個方法,參數說明如下: nonRoot=false→ 僅當activity為task根(即首個activity例如啟動activity之類的)時才生效 nonRoot=tru ...
  • Android中,如果想把stack中的某個Activity moveTaskToFront,可以定義一個BroadcastReceiver接收某種類型的Broadcast,併在onReceive中start該Activity。但是需要註意的是,startActivity前面的context參數問題 ...
  • NB_10分鐘10行代碼_做出來的程式讓人驚嘆 (10分鐘瞭解Delphi XE全平臺開發流程 ) 超清晰下載地址: http://info.lm.tv.sohu.com/redirect.do?wec=MTQl6w3n5xtZKh-ZkssiPvnT2lqhm4iEqmI1U0jIphc5Ll8r ...
  • 阿裡聚安全在之前的三篇博客中介紹了利用Frida攻擊Android應用程式,整個過程仿佛讓開發者開啟上帝視角,在本篇博客中,我們將會介紹在iOS應用程式中使用Frida繞過越獄檢測。即使你從來沒有使用過Frida,這篇文章也將會作為進入移動安全開發和分析的入門指南。 相關文章內容: 利用FRIDA攻 ...
  • 基於RxPaparazzo的圖片裁剪,圖片旋轉、比例放大|縮小。涉及Material Design+Cardview+butterknife,基於rxjava2的RxPaparazzo等...... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...