您好,登錄后才能下訂單哦!
這篇文章給大家介紹如何進行JNI的使用,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
Java Native Interface(JNI) 是一種使用java語言和原生C/C++語言相互調用、混合編程的方法,它允許在Java虛擬機(VM)內運行的Java代碼與應用其他編程語言(如C、C++和匯編)編寫的應用程序和庫進行互操作,它支持從動態鏈接庫中加載代碼, 并能使用C/C++的高效的特性。
如果要基本了解JNI的功能與使用,可以閱讀Java Native Interface文檔https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html
同時,本文將介紹一些在可能在剛開始會注意不到的使用細節。
JNI類名由包名開始, 由'/'分隔,例如
”java/lang/String”
如果要查找的是一個數組類, 類名應為簽名形式,所以一個一維String數組的類定義為
“[Ljava/lang/String;”
可以參照https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp9502
但是目前即使全都傳入簽名形式的類名,FindClass依然會找到對應的類。原因是歷史遺留問題,參考https://bugs.openjdk.java.net/browse/JDK-6411605
為了保證正確性,可以在運行class文件時,加參數-Xcheck:jni可以校驗類名構造是否規范。
在函數重載和內部類類型作為某個重載函數的參數時,會觸發javah生成的函數名不正確的問題,javah會按照JNI的名稱解析規則生成JNI函數名,但是JVM在運行時卻不會按照這個名字查找函數,因此會出現運行時找不到函數的情況。
參考https://bugs.openjdk.java.net/browse/JDK-8145897
package p;
class A {
static class B {}
static class C {
native static long Foo(B bar);
}
}
運行javah -jni -o jni_libA.h -classpath ./ p.A
預期的輸出是:
//Method: Foo
//Signature: (Lp/A$B;)J
JNIEXPORT jlong JNICALLJava_p_A_00024C_Foo__Lp_A_00024B_2
但實際會輸出:
//Method: Foo
//Signature: (Lp/A/B;)J
JNIEXPORT jlong JNICALLJava_p_A_00024C_Foo__Lp_A_B_2
內部類B的’$’被忽略。
可選的解決方法有兩種:
1. 不把有內部類作為參數的重載函數寫入本地方法
2. 在生成方法后手動添加’$’(‘00024’)
在有異常等待處理時不能調用大多數的JNI方法。要通過函數ExceptionCheck或ExceptionOccurred的返回值預期到有異常并返回,或處理并清除異常。
如果要獲得異常的描述,需要找到Throwable類,然后調用它的getMessage "()Ljava/lang/String;"方法,用GetStringUTFChars來獲取內容。
很多JNI方法會拋出異常,但有些異常不會被拋出,需要在應當拋出異常的地方添加條件判斷,用ThrowNew構造異常,最常見的是java.lang.ArithmeticException與 Java.lang.NullPointerException
如果想對數組進行寫入或讀出,Get<Type>ArrayElements和GetStringChars是非常有用的方式。
Get<PrimitiveType>ArrayElements系列函數允許返回一個指向實際元素的指針,或者分配一些內存來拷貝數據。 返回的原始數據的指針在調用釋放方法前是保證一直有效的,而且必須手動釋放每個獲取的數組。同時可以通過傳一個非空指針作為isCopy的參數來決定是否拷貝數據。
jbyte* data = env->GetByteArrayElements(array, NULL);
if (data != NULL) {
memcpy(buffer, data, len);
env->ReleaseByteArrayElements(array, data, JNI_ABORT);
}
上面的代碼是一個簡單的舉例,獲取了數組,然后拷貝了len長度的byte,最后釋放掉數組。
還有一個更簡單實現同樣功能的方式是
env->GetByteArrayRegion(array, 0, len, buffer);
這種方式有幾個好處:
1. 只需要一個JNI調用,減少開銷.
2. 不需要對原始數據進行限制或者額外的拷貝數據
3. 減少在某些出錯后忘記釋放的風險
4. 會在越界時拋出異常
類似的,Set<Type>ArrayRegion系列方法是將數組中的元素賦值, GetStringRegion或者GetStringUTFRegion是獲得String中的字符。
通付盾SDK保護系統應用輸入為aar包,通過分析aar結構,將待保護的class文件轉換為AST(抽象語法樹),對AST進行同態翻譯,將其轉換為對應的本地代碼。本地化的轉換規則與AST節點數目一一對應。由于字節碼是托管代碼,運行于VM中(java代碼運行于JVM,安卓字節碼運行在Dalvik),對于操作VM內存模型,以及涉及VM特性的行為,如創建對象,拋出異常等操作,本地代碼無法表達對應的語義。對于帶有這樣語義的AST節點,本地代碼的翻譯遵循JNI調用規范,在確保本地化翻譯的正確性的同時兼顧了兼容性。
關于如何進行JNI的使用就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。