Android JNI&NDK程式設計小結及建議
前言
由於網上關於JNI/NDK相關的知識點介紹的比較零散而且不具備參照性,所以寫了這篇JNI/NDK筆記,便於作為隨時查閱的工具型別的文章,本文主要的介紹了在平時專案中常用的命令、JNI資料型別、簽名等,便於查閱相關資料。文末相關參考資料比較適合剛接觸或者不熟悉Android NDK開發的朋友參閱。
常用命令
javac 編譯java原始檔生成.class檔案
由於JNI對應的標頭檔案由javah工具根據對應的.class檔案生成,所以在進行JNI程式設計之前,寫好Java程式碼後需要先編譯,在使用javah生成對應的標頭檔案
javah -jni自動生成標頭檔案
舉例說明:
-
生成普通的JNI標頭檔案
javah -classpath path -jni -d outputdirpath com.mrljdx.JavaNativeCode
-
在Java函式中包含Android相關的引數程式碼,則需要在classpath中新增android.jar包的絕對路徑地址
javah -classpath path:$ANDROID_HOME/path/android.jar -jni -d outputdirpath com.mrljdx.JavaNativeCodeWithAndroid
javap -s -p 檢視函式簽名
-s: 顯示簽名(只顯示public型別的簽名) -p:顯示所有函式、成員變數的簽名
舉例說明:
javap -classpath pacakage_path_dir -s -p com.mrljdx.JavaCode
JNI資料型別和型別簽名
資料型別
JNI的資料型別包括:基本型別和引用型別。這一點和Java的語言特性一致,基本型別包括jboolean、jchar、jint、jlong、jbyte、jshort、jfloat、jdouble、void,與Java型別的對應關係如下:
JNI型別 | Java型別 | 描述 |
---|---|---|
jboolean | boolean | 無符號8位整型 |
jbyte | byte | 有符號8位整型 |
jchar | char | 無符號16位整型 |
jshort | short | 有符號16位整型 |
jint | int | 32位整型 |
jlong | long | 64位整型 |
jfloat | float | 32位整型 |
jdouble | double | 64位整型 |
void | void | 無型別 |
JNI中引用型別主要有類、物件和陣列,這點也上符合Java的語法規範,對應的關係如下:
JNI 型別 | Java引用型別 | 描述 |
---|---|---|
jobject | Object | Object型別 |
jclass | Class | Class型別 |
jstring | String | String型別 |
jobjectArray | Object[] | 物件陣列 |
jbooleanArray | boolean[] | boolean陣列 |
jbyteArray | byte[] | byte陣列 |
jcharArray | char[] | char陣列 |
jshortArray | short[] | short陣列 |
jintArray | int[] | int陣列 |
jlongArray | long[] | long陣列 |
jfloatArray | float[] | float陣列 |
jdoubleArray | double[] | double陣列 |
jthrowable | Throwable | Throwable |
JNI型別簽名
JNI的型別簽名標識了一個特定的Java型別,這個型別可以是類和方法,也可以是資料型別。
型別簽名
類的簽名採用”L+包名+類名+;”標識,包名中將.
替換為/
即可。
比如String類的簽名:Ljava/lang/String;
注意末尾的;
屬於簽名的一部分。
再比如Android中Context類的簽名:Landroid/content/Context;
基本資料型別簽名
基本資料型別的簽名採用一系列大寫字母來標識,如下:
Java型別 | 簽名 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
可以發現除了 long
基本資料型別的簽名為J
之外其他的都比較容易辨識,估計是由於之前的類型別的簽名開頭為L+包名+類名+;
設計者為了區分所以簽名為J
陣列的型別簽名
陣列的型別簽名比起類型別和基本資料型別的要稍微複雜一點,不過還是很好理解的。對於陣列來說,它的簽名為[+型別簽名
,舉例說明:
String[] 陣列型別對應的簽名:[Ljava/lang/String;
可以發現,就是在String的類簽名前加了個[
同理基本資料型別簽名int[]的簽名:[I
注意這裡基本型別後面是不帶分號的。
那麼多維陣列呢?可以類推,int[][] 的簽名為[[I
,而String[][]的簽名為[[Ljava/lang/String;
方法的簽名
在JNI中會經常需要在C/C++程式碼中呼叫Java的函式,這時候就會用到方法的簽名。方法的簽名為(+引數型別簽名+)+返回值型別簽名
,比如:
方法:boolean login(String username,String password)
的方法簽名如下:(Ljava/lang/String;Ljava/lang/String)B
如果這裡不理解的話,請再去看看之前關於基本型別,類型別的簽名部分內容。
小技巧:使用 類似於
javap -classpath pathdir -s -p com.sample.JavaCode
的 javap -s -p 命令也可以幫助檢視一些類中各種方法和成員變數的簽名。
JNI相關命名解釋
函式名的格式遵循規則:
Java_包名_類名_方法名
JNIEXPORT、JNICALL、JNIEnv和jobject 都是JNI標準中所定義的型別或者宏
JNIEnv * : 指向JNI環境的指標,可以透過JNIEnv * 訪問JNI提供的介面方法
JNIEXPORT、JNICALL:是jni.h中所定義的宏。
注:JNIEnv * 可以簡單的理解為Java和C/C++ 之間相互呼叫的橋樑,我們可以透過JNIEnv * 呼叫C/C++定義的方法,也可以在C/C++中透過JNIEnv * 來呼叫Java類中的方法。下面將會講到C/C++中呼叫Java的方法,注意JNIEnv *的作用。
在C/C++中呼叫Java方法
首先說明一點,在Android開發過程中使用NDK主要是為了提高程式碼的安全性,有些遊戲公司可能是為了方便利用已有的C/C++開源庫來進行平臺移植,其實在效能提升方面,NDK的作用並不是很明顯。所以有時候一些在Java中實現起來非常簡單的程式碼放在JNI裡面做會顯得吃力不討好,所以乾脆就直接在JNI中呼叫Java的方法,我們只把加密和驗證的一些邏輯寫到JNI層就行了。
在JNI中呼叫Java方法流程如下:
在Java中定義一個靜態方法供JNI呼叫,注意要是靜態的。
在JNI中利用env來呼叫Java中定義的靜態方法
呼叫宣告好的靜態方法
可能流程說的比較抽象,用程式碼簡單說明一下:
-
定義靜態方法:
//對應包名:com.mrljdx.jni.HelloJNIpublic static void helloJava() { System.out.println("Hello JavaCode"); }
-
JNI宣告靜態方法:
static void static_helloJava(JNIEnv *env){ jclass clazz = env->FindClass("com/mrljdx/jni/HelloJNI"); jmethodID mid = env->GetStaticMethodID(clazz, "helloJava", "()V"); env->CallStaticVoidMethod(clazz, mid); }
-
呼叫宣告好的靜態方法:
static_helloJava(env);
在AndroidStudio中NDK程式設計配置注意事項:
-
在專案的
gradle.properties
中新增ndk支援:android.useDeprecatedNdk=true
-
配置
build.gradle
看程式碼註釋:defaultConfig { minSdkVersion 9 targetSdkVersion 23 versionCode 1 versionName "1.0" //配置ndk 支援 ndk { //編譯的so庫名稱 libsecurity.so moduleName "security" //指定編譯後的庫支援的平臺 abiFilters "armeabi", "mips", "x86", "armeabi-v7a" //用於指定應用應該使用哪個標準庫,此處新增c++庫支援 stl "stlport_static" } }
在AndroidStudio中寫JNI程式碼有一個比較爽的地方,就是Android.mk系統會在編譯時自動幫你生成,你只需要配置build.gradle就行了。注意jni相關程式碼需要放在
src/main/jni
目錄下。如果對gradle配置不瞭解可以參考我的部落格:
小結
在我們做產品的時候,應該考慮該用JNI&NDK的時候就用,一切出發點是基於使用者的體驗和資料安全,我覺得在以下幾種情況下建議使用NDK:
重用現有的程式碼,比如C/C++的程式碼在Android中的重用。
資料安全,比如將Http的請求加密和解密演算法放在NDK中去實現,這樣可以提高應用的安全。
提升效能,由於Android裝置製造商在手機中給每個應用分配了可用的最大RAM,有時候為了效能考慮,可以透過Native程式碼向系統來“借”一些記憶體,儘量少的使用系統分配給應用的記憶體。(參考Infoq:)
參考文章及書籍
: 一本不厚的書,關於Android相關的效能最佳化建議都是滿滿的乾貨,適合有一定開發經驗的人參考。
: 關於google官方的培訓課程提出了在使用NDK做本地開發時的一些最佳化建議。
: 一個系列的文章,適合剛接觸JNI/NDK開發的朋友學習。
:關於JNI的介紹比少,如果你對Android開發想深入理解一些View衝突、屬性動畫那麼可以推薦一看。如果只想學習JNI相關內容,推薦看 的內容。
END
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3549/viewspace-2814778/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android程式設計師的Java後臺學習建議Android程式設計師Java
- 程式設計幾點建議程式設計
- 如何自學程式設計及Java、上手真實開發及轉行程式設計師的建議Java行程程式設計師
- 程式設計漫談(二十):如何自學程式設計及Java、上手真實開發及轉行程式設計師的建議Java行程程式設計師
- Java gc(垃圾回收機制)小結,以及Android優化建議JavaGCAndroid優化
- 學習Java程式設計的建議Java程式設計
- 非同步程式設計小結非同步程式設計
- Midway 後端程式碼的設計建議後端
- 設計模式大雜燴(24種設計模式的總結及學習設計模式的幾點建議)設計模式
- python程式設計規範系列–建議01~07Python程式設計
- 併發程式設計模型小結程式設計模型
- 構建小程式總結
- 給程式設計師“菜鳥”的6條建議程式設計師
- 小程式的專案結構設計
- 程式設計模型(正規化)小結程式設計模型
- python核心程式設計:入門Python程式設計的8個實踐性建議Python程式設計
- Android開發一點小技巧和建議獻上Android
- 【介面功能設計】TopThink介面功能設計建議
- 有哪些讓程式設計師受益終生的建議程式設計師
- ? 分享8點超級有用的Python程式設計建議Python程式設計
- 1024 寫給程式設計師的一些建議程式設計師
- javascript 設計模式(修言小冊乾貨,長文建議收藏)JavaScript設計模式
- 微信小程式應用安全分析及設計微信小程式
- 實施PLM系統的總結及建議
- #給java程式設計師的10條建議,吐血推薦!Java程式設計師
- 給各位PHP程式設計師十點未來的建議PHP程式設計師
- 2020年為什麼建議你繼續學程式設計?程式設計
- 結對程式設計——小學四則運算練習題小程式程式設計
- F2P手遊設計的五個誤區及建議
- shell script程式設計小結——附帶例項程式設計
- 結對程式設計 小學四則運算程式設計
- 總結 90 條寫 Python 程式的建議Python
- 程式設計小記-程式設計規範程式設計
- 關於要不要轉行做程式設計師的建議程式設計師
- 【程式設計小技巧】程式設計
- android USB host程式設計Android程式設計
- 015 Rust非同步程式設計錄製階段性結束和Rust後續學習建議Rust非同步程式設計
- .NET程式設計5月小結 - Blazor, Unity, Dependency Injection程式設計BlazorUnity