您好,登錄后才能下訂單哦!
為什么需要JNI?
android這個龐大的系統從下到上主要由linux內核,C/C++庫,java應用程序框架,java應用程序組成。這就涉及到一個問題,C/C++庫如何與java應用有交集,或者說能相互調用,要解決這個問題,就需要JNI登場了。
JNI調用機制分析
JNI--java native interface,翻譯成中文是java本地接口,所謂的“本地”是指C/C++庫一層的C/C++語言(以下統稱C)。
上文提到,JNI是為解決C和Java相互調用的問題而誕生的。C和Java相互調用無非就是兩個方面,Java調用C和C調用Java。
Java調用C函數
如果你只定義了一個函數,而將它的實現交給C,那么就將它定義為native類型好了。你或許會問了,我用Java定義了一個函數,但我卻沒用Java實現之,當我調用這個函數的時候,Java編譯器不會報錯嗎?答案是否定的,Java編譯器在遇到native類型的函數時,不會關心該函數的具體實現,相當于native類型告訴Java編譯器,“喂,老兄,我實現了,只不過我不是用你的語言(Java)實現的,我是用別的語言(C)實現的”,所以編譯時Java編譯器不會報錯,只不過在調用native類型的函數前,程序員必須把C生成的動態庫裝載進內存,否則程序會因為找不到相應的native方法而出錯。
Java和C本不是同種語言,硬要讓它們能相互調用的話,我們就要遵循某種規范(就像ARM匯編和C相互調用時也要遵循某種規范一樣)。
規范:methodname_C = Packagename + methodname_Java。什么意思呢?在C中定義的函數名稱 = 包名 + 在Java中定義的函數名稱。還不明白?我們來舉個例子來看一下。
java:private native final void init()
C:static void android_content_AssetManager_init(JNIEnv *env,jobject clazz)
可以看到對應的C函數多了兩個參數,看起來莫名其妙,但這其實能讓C訪問Java對象或函數,JNIEnv對象是一個Java虛擬機所運行的環境,jobject是調用該函數的對象。我們稍后會講到。
為什么C中的函數要加上包名呢?這不是多此一舉嗎?
這是有必要的,這是為了區分開不同包的兩個同名函數,能夠讓Java編譯器找到正確的那個函數。事實上,Java調用native函數時,編譯器會向native引擎傳遞調用者的包名,以及函數名稱,還有參數類型,以便可以根據這些信息找到正確的本地的那個函數。
C調用Java函數或者訪問Java變量
正如Java調用C函數一樣,Java把類名、函數名稱和參數類型傳遞給native引擎,然后由native引擎處理(正確找到并調用)C函數。同理,C調用Java時,也需要把想要訪問的類名、函數名稱和參數傳遞給Java引擎。步驟如下:
1)獲取Java對象的類
jclass cls = env->GetObjectClass(jobject),還記得env和jobject嗎?沒錯,就是那兩個參數。現在知道那兩個參數的重要性了吧。不過,這也意味著C調用Java函數只能在Java調用C函數體中進行。
2)獲取Java函數的id值
jmethodId mid = env->GetMethodId(cls,"methodname","(Ljava/lang/String;)V"),前兩個參數不必說,第三個參數值得注意,它代表了Java函數的參數和返回值,參數在括號之中,返回值在括號之外。
3)找到了函數后,就可以調用該函數了
env->CallXXXMethod(jobject,mid,ret),其中XXX代表了返回值的類型,
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。