1. 程式人生 > >Android安全/開發基礎--8--Java本地介面(JNI)

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釋放。弱引用可以跨方法、執行緒使用。