JAVA进阶day07JNI(java调用c)A部分
目前接觸的JNI有java調用c和c調用java兩類。其中java調用c又有隱式和顯示兩種映射關系。本筆記針對java調用c的顯示映射。本著工程實際夠用的原則,不夠再回頭來補充。
JAVA訪問c庫需要有三個步驟
1:加載C庫
2:建立java函數名到c庫函數名的映射關系
3:在java程序里調用函數
先貼代碼,依照代碼來做分析:
分條縷析做筆記:
- 1:加載C庫和在java程序里調用函數
代碼中在靜態代碼塊中進行加載c庫。可以知道我們要加載的是libnative.so庫。放到靜態代碼塊中可以保證該加載能夠只執行一次。非常棒的處理。
public native void hello();native修飾的hello表明這個hello方法不是在java中實現的而是在本地語言中實現的。我們在使用hello函數的時候首先要進行聲明。這個聲明也可以聲明為靜態的,比如public native static void hello();這時候就沒有必要先實現JNIDemo的實例化靜態對象了,也就是說我們可以省掉JNIDemo d=new JNI Demo();這個處理,直接使用JNIDemo.hello();當然我們的main函數也是在JNIDemo類中的,可以直接寫為hello()。問題又來了,那么同樣的道理為什么不是靜態的還需要實現JNIDemo對象呢,這個也是在JNIDemo的main函數中啊?這個就沒必要較真了,記住就好,又不是讓你去創造java語言,你只不過是用這個工具而已。我還發現這個public native static void hello()跟public static native void hello()是一個效果。無礙乎這個static跟native的前后。到此我們對這個加載c庫就算是掌握了。
- 2:建立java函數名到c庫函數名的映射關系
為了將java中的hello跟c語言中的函數對應起來,這部分的處理是c語言中做的。我們姑且猜測,c語言在什么時候去做這個映射對應呢?我提出一個想法,我們在加載SystemloadLibrary的時候會引起c庫文件中的一個觸發,在那個觸發函數里面做了對應。那么我們從文件中的結構應該是包含 觸發 實現。我們貼代碼分析:
我們看到,果不其然跟我們猜測的一模一樣。
的確是分為兩部分。最頂層那部分實現了c_hello函數,最下半部分實現了一個JNI_OnLoad觸發函數。哇咔咔,一切都這么順滑。那我們開墾這個觸發函數吧。
該函數功能比較單一所以寫法肯定是固定的。每次使用我們只需要去修改這么幾個點就可以了:
JNI_VERSION_1_4 這個代表的是你使用的jni的版本。
這個作用就是從env找到JNIDemo這個類,方面后面我們去做映射。故而這個JNIDemo我們做移植的時候是需要去修改的。
if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)這個地方也就是我們想要的注冊了。后面的1代表你要注冊的個數。
我們再看看這個注冊methods里面的實現。
這個模式也是固定的
其中“hello”對應的是java中的函數名字。c_hello對應的c文件中的函數名字。前面的void * 是永遠不變的。中間的這個類型signature我是打死也不想記住怎么寫的。我們可以在寫完java文件之后使用javac JNIDemo.java進行編譯然后使用
javah -jni JNIDemo
生成一個JNIDemo.h文件
我們從里面摘取
/** Class: JNIDemo* Method: hello* Signature: ()V*/就知道這個signature 就是()V.
同時我們知道我們的c文件的hello函數書寫形式應該類似與
實際中我們的c語言函數書寫是
void c_hello(JNIEnv *env, jobject cls)幾乎是一樣的,其中jobject 跟jclass我覺得應該是java編譯器不同導致的,我感性的認為應該是兼容的,都能用。既然我的編譯器出來的是jclass那么我的代碼就修改為
void c_hello(JNIEnv *env, jclass cls)寫到這里,還剩下兩件事,怎樣編譯c文件為lib*.so以及java同c語言文件如何傳遞返回數據類型。我們知道java跟c的數據類型上是有區別的。包括開辟的空間大小等。最起碼的java中有string類型,然而我們的c語言中就沒有。因此,這部分還算是一個不大不小的知識點。按下不表下集再說。
總結
以上是生活随笔為你收集整理的JAVA进阶day07JNI(java调用c)A部分的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言完数求解
- 下一篇: 域用户权限|安装软件