1:JNI是什嗎?
Java NativeInterface(JNI)是Java提供的一個很重要的特性。它使得用諸如C/C++等語言編寫的代碼可以與運行於Java虛擬機器(JVM)中的 Java代碼整合。有些時候,Java並不能滿足你的全部開發需求,比如你希望提高某些關鍵模組的效率,或者你必須使用某個以C/C++等Native語 言編寫的程式庫;此時,JNI就能滿足你在Java代碼中訪問這些Native模組的需求。JNI的出現使得開發人員既可以利用Java語言跨平台、類庫豐 富、開發便捷等特點,又可以利用Native語言的高效。
2:JNI和JVM什麼關係?
JNI是JVM實現中的一部分,因此Native語言和Java代碼都運行在JVM的宿主環境(Host Environment)。此外,JNI是一個雙向的介面:開發人員不僅可以通過JNI在Java代碼中訪問Native模組,還可以在 Native代碼中嵌入一個JVM,並通過JNI訪問運行於其中的Java模組。可見,JNI擔任了一個橋樑的角色,它將JVM與Native模組聯絡起來,從而實現了Java代碼與Native代碼的互訪。在OPhone上使用Java虛擬機器是為嵌入式裝置特別最佳化的Dalvik虛擬機器。每啟動一個應 用,系統會建立一個新的進程運行一個Dalvik虛擬機器,因此各應用實際上是運行在各自的VM中的。Dalvik VM對JNI的規範支援的較全面,對於從JDK 1.2到JDK 1.6補充的增強功能也基本都能支援。
開發人員在使用JNI之前需要充分瞭解其優缺點,以便合理選擇技術方案實現目標。JNI的優點前面已經講過,這裡不再重複,其缺點也是顯而易見的:由於Native模組的使用,Java代碼會喪失其原有的跨平台性和型別安全等特性。此外,在JNI應用中,Java代碼與Native代碼運行於同一個進程空間內;對於跨進程甚至跨宿主環境的Java與Native間通訊的需求,可以考慮採用socket、Web Service等IPC通訊機制來實現。
3:JNI在JAVE和c++中互通性;
a:java和c++的基本調用:
b:JNIhelper的概念和用法:
就是編譯的時候,會出現編譯的error,想調試的哥們,try一下;問題不大;
c:獨立JNIhelper的使用:
4:JNI的編譯
Android.mk的編譯配置:
編譯的基本命令:
5: JNI用法品讀
(1).andorid CPP調用java函數和訪問其成員:
原理 => CPP代碼找到java的那個class裡面的函數的入口地址,然後在CPP代碼中調用java代碼
步驟1) 用FindClass()函數找到該java類(如android.os.Binder)的執行個體對象的引用:
jclass clazz =env->FindClass(kBinderPathName) =env->FindClass("android.os.Binder")
步驟2) 用GetFieldID()函數擷取到要訪問的域(field: 實際上就是該java class中的某個成員變數的名字)的ID:
gBinderOffsets.mObject= env->GetFieldID(clazz, "mObject", "I") // mObject為java class "Binder"裡的一個成員變數
-> 注意,這裡將要訪問的那個java對象的成員mObject的ID儲存到了全域變數gBinderOffsets.mObject中,這樣做的前提和優點如下:
前提: android裡面,每個java進程中只允許有一個java虛擬機器(sun公司原始的java架構中,一個進程中可以有多個java虛擬機器)
優點: 除了第一次,以後每次要訪問該java對象的成員mObject就非常快了(不用再去FindClass()和GetFieldID())
步驟3) 用GetMethodID()函數擷取到要訪問的方法(Method: 實際上就是該java class中的某個成員函數的名字)的ID:
gBinderOffsets.mExecTransact= env->GetMethodID(clazz, "execTransact", "(IIII)Z") //execTransact為java class "Binder"裡的一個成員函數
步驟4) 用類似於GetIntField()的函數擷取到該java對象的那個域(即成員)的值:
IBinder* target =(IBinder*)env->GetIntField(obj,gBinderProxyOffsets.mObject)
// 擷取javaandroid.os.Binder類型對象裡面的成員mObject的值
步驟5) 用類似於CallBooleanMethod()的函數調用到該java對象的那個成員函數:
jboolean res =env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, code,(int32_t)&data, (int32_t)reply, flags)
(2).android java調用CPP函數:
原理 => 相當於java的那個class裡面有的函數使用CPP代碼來實現了
1)通過結構JNINativeMethod描述java代碼調用函數和CPP函數的對應關係:
typedef struct {
const char* name; //java代碼調用CPP函數的入口
const char*signature; // CPP函數的傳回值
void* fnPtr; // CPP的函數名
} JNINativeMethod;
=> 例如: java代碼通過IBinder.transact()來調用CPP的函數android_os_BinderProxy_transact()
{"transact","(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z",(void*)android_os_BinderProxy_transact},
2)將CPP函數註冊到java的某個class中: 使用函數AndroidRuntime::registerNativeMethods()來註冊
=> 這之後,java代碼就可以調用CPP函數了
3)java代碼調用CPP函數方法:
IBinder.transact()
總結其原理:C/C++要調用JAVA程式,必須先載入JAVA虛擬機器,由JAVA虛擬機器解釋執行class檔案。為了初始化JAVA虛擬機器,JNI提供了一系列的介面函數,通過這些函數方便地載入虛擬機器到記憶體中。