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