Android安全/開發基礎--8--Java本地介面(JNI)
8-1、JNI概述
JNI的本意是Java Native Interface(Java本地介面),是為了方便Java和C/C++等原生代碼所封裝的一層介面,使用JNI技術可以對Java層遮蔽不同作業系統平臺之間的差異,從而實現Java本身的平臺無關特性。JNI和Android是沒有關係的。通過JNI可以做到以下兩點:
1、Java程式中的函式可以呼叫Native語言寫的函式,Native一般指的是C/C++寫的函式。 2、Native程式中的函式可以呼叫Java層的函式,即C/C++程式中可以呼叫Java的函式。
以MediaScanner為例:
MediaScanner完成android中的多媒體檔案的掃描工作。得到如歌曲專輯名,流派,歌曲時長等資訊,並將它們儲存在媒體資料庫中,供其它應用程式使用。
1、Java世界對應的是MediaScanner,而這個MediaScanner類有一些函式需要由Native層來實現。
2、JNI層對應的是libmedia_jni.so,media_jni是JNI庫的名字,其中下劃線前的media是Native層庫的名字,這裡就是libmedia庫,下劃線後的jni表示這是一個JNI庫。
3、Native層對應的是libmedia.so,這個庫完成了實際的功能。
4、MediaScanner將通過JNI庫libmedia_jni.so和Native層的libmedia.so互動。
總結:
即JNI層必須實現為動態庫(執行時載入的庫)的形式,這樣Java虛擬機器才能載入它並呼叫它的函式。
Java層的MediaScanner:
是一個.java檔案。在呼叫native函式前,任何時候任何地方都可以載入動態庫。
JNI層的MediaScanner:
是一個.cpp檔案。註冊JNI函式就是將java層的native函式和JNI層對應的實現函式關聯起來。
1、靜態註冊:
根據函式名來建立Java函式和JNI函式之間的關聯關係,而且要求JNI層函式的名字必須遵循特定的格式。
2、動態註冊:
使用JNINativeMethod的結構來儲存Java native函式和JNI函式的對應關係。當Java層通過System.loadLibrary載入完JNI動態庫後,緊接著會查詢該庫中的JNI_OnLoad函式。如果有就呼叫它,動態註冊的工作就是在這裡完成的。
8-2、資料型別轉換
在Java中呼叫native函式傳遞的引數是Java資料型別,這些引數型別到了JNI層會發生轉換。如下: 基本資料型別轉換關係表:
Java | Native型別 | 符號屬性 | 字長 |
---|---|---|---|
boolean | jboolean | 無符號 | 8位 |
byte | jbyte | 無符號 | 8位 |
char | jchar | 無符號 | 16位 |
short | jshort | 有符號 | 16位 |
int | jint | 有符號 | 32位 |
long | jlong | 有符號 | 64位 |
float | jfloat | 有符號 | 32位 |
double | jdouble | 有符號 | 64位 |
引用資料型別轉換關係表:
Java引用型別 | Native型別 | Java引用型別 | Native型別 |
---|---|---|---|
All objects | jobject | char[] | jcharArray |
java.lang.Class例項 | jclass | short[] | jshortArray |
java.lang.String例項 | jstring | int[] | jintArray |
Object[] | jobjectArray | long[] | jlongArray |
boolean[] | jbooleanArray | float[] | jfloatArray |
byte[] | jbyteArray | double[] | jdoubleArray |
java.lang.Throwable例項 | jthrowable | – | – |
8-3、JNI/NDK開發流程
Java JNI的解釋見本章首,JNI和Android是沒有關係的。
NDK是Android提供的一個工具集合,幫助開發者快速開發C/C++的動態庫,並能自動將so和java應用一起打包成apk。它還集成了交叉編譯器,並提供了相應的mk檔案隔離CPU、平臺、ABI等差異,開發人員只需要簡單修改mk檔案,就可以創建出so。
JNI的開發流程:
1、在Java中宣告native方法 2、編譯Java原始檔得到class檔案,然後通過javah命令匯出JNI的標頭檔案 3、實現JNI方法。JNI方法是指Java中宣告的native方法,選擇C/C++來實現 4、編譯so庫並在Java中呼叫
NDK的開發流程:
1、下載並配置NDK。注意新增環境變數
2、建立一個Android專案,並宣告所需的native方法
3、實現Android專案中所宣告的native方法
4、切換到jni目錄的父目錄,然後通過ndk-build命令編譯產生so庫
JNI呼叫Java方法的流程:先通過類名找到類,再根據方法名找到方法的id,最後呼叫這個方法。如果是呼叫Java中的非靜態方法,那麼需要構造出類的物件後才能呼叫它。
8-4、JNI中的引用
JNI支援三種引用:區域性引用、全域性引用、弱全域性引用
區域性引用和全域性引用有不同的生命週期。當本地方法返回時,區域性引用會被自動釋放,而全域性引用和弱引用必須手動釋放。區域性引用或者全域性引用會阻止垃圾回收(GC)它們所引用的物件,而弱引用則不會。不是所有的引用可以被用在所有的場合。例如一個本地方法建立一個區域性引用並返回後,再對這個區域性引用進行訪問是非法的。
區域性引用:
區域性引用只有在建立它的本地方法返回前有效。JNIapi中有三個函式對區域性引用進行處理:EnsureLocalCapacity、PushLocalFrame、PopLocalFrame。
全域性引用:
全域性引用可以跨方法、跨執行緒使用,直到它被手動釋放才會失效。
弱引用:
弱引用使用NewGlobalWeakRef建立,使用DeleteGlobalWeakRef釋放。弱引用可以跨方法、執行緒使用。