1. 程式人生 > >java與C/C++之間通過jni相互呼叫

java與C/C++之間通過jni相互呼叫

一、jni簡介:

企業應用

JNI一直以來都很少去關注,但卻是我心中的一個結,最近這幾天剛好手頭有點時間,因此抽空看了一下這方面的東西,整理了一份文件,JNI技術的出現主要是基於三個方面的應用需求:

1. 解決效能問題
Java具有平臺無關性,這使人們在開發企業級應用的時候總是把它作為主要候選方案之一,但是效能方面的因素又大大削弱了它的競爭力。為此,提高Java的效能就顯得十分重要。Sun公司及Java的支持者們為提高Java的執行速度已經做出了許多努力,其中大多數集中在程式設計的方法和模式選擇方面。由於演算法和設計模式的優化是通用的,對Java有效的優化演算法和設計模式,對其他編譯語言也基本同樣適用,因此不能從根本上改變Java程式與編譯型語言在執行效率方面的差異。由此,於是人們開始引入JIT(Just In Time,及時編譯)的概念。它的基本原理是:首先通過Java編譯器把Java原始碼編譯成平臺無關的二進位制位元組碼。然後在Java程式真正執行之前,系統通過JIT編譯器把Java的位元組碼編譯為本地化機器碼。最後,系統執行本地化機器碼,節省了對位元組碼進行解釋的時間。這樣做的優點是大大提高了Java程式的效能,縮短了載入程式的時間;同時,由於編譯的結果並不在程式執行間儲存,因此也節約了儲存空間。缺點是由於JIT編譯器對所有的程式碼都想優化,因此同樣也佔用了很多時間。

動態優化技術是提高Java效能的另一個嘗試。該技術試圖通過把Java源程式直接編譯成機器碼,以充分利用Java動態編譯和靜態編譯技術來提高Java的效能。該方法把輸入的Java原始碼或位元組碼轉換為經過高度優化的可執行程式碼和動態庫 (Windows中的. dll檔案或Unix中的. so檔案)。該技術能大大提高程式的效能,但卻破壞了Java的可移植性。

JNI(Java Native Interface, Java本地化方法)技術由此閃亮登場。因為採用JNI技術只是針對一些嚴重影響Java效能的程式碼段,該部分可能只佔源程式的極少部分,所以幾乎可以不考慮該部分程式碼在主流平臺之間移植的工作量。同時,也不必過分擔心型別匹配問題,我們完全可以控制程式碼不出現這種錯誤。此外,也不必擔心安全控制問題,因為Java安全模型已擴充套件為允許非系統類載入和呼叫本地方法。根據Java規範,從JDK 1. 2開始,FindClass將設法找到與當前的本地方法關聯的類載入器。如果平臺相關程式碼屬於一個系統類,則無需涉及任何類載入器; 否則,將呼叫適當的類載入器來載入和連結已命名的類。換句話說,如果在Java程式中直接呼叫C/C++語言產生的機器碼,該部分程式碼的安全性就由Java虛擬機器控制。

2. 解決本機平臺介面呼叫問題
JAVA以其跨平臺的特性深受人們喜愛,而又正由於它的跨平臺的目的,使得它和本地機器的各種內部聯絡變得很少,約束了它的功能。解決JAVA對本地操作的一種方法就是JNI。JAVA通過JNI呼叫本地方法,而本地方法是以庫檔案的形式存放的(在WINDOWS平臺上是DLL檔案形式,在UNIX機器上是SO檔案形式)。通過呼叫本地的庫檔案的內部方法,使JAVA可以實現和本地機器的緊密聯絡,呼叫系統級的各介面方法。

3. 嵌入式開發應用
“一次程式設計,到處使用”的Java軟體概念原本就是針對網上嵌入式小裝置提出的,幾經周折,目前SUN公司已推出了J2ME(Java 2 P1atform Micro Edition)針對資訊家電的Java版本,其技術日趨成熟,開始投入使用。SUN公司Java虛擬機器(JVM)技術的有序開放,使得Java軟體真正實現跨平臺執行,即Java應用小程式能夠在帶有JVM的任何硬軟體系統上執行。加上Java語言本身所具有的安全性、可靠性和可移植性等特點,對實現瘦身上網的資訊家電等網路裝置十分有利,同時對嵌入式裝置特別是上網裝置軟體程式設計技術產生了很大的影響。也正是由於JNI解決了本機平臺介面呼叫問題,於是JNI在嵌入式開發領域也是如火如荼。

不失直觀性,我們首先寫一個JNI小例子:

Java程式碼  收藏程式碼
  1. public class HelloJni {  
  2.     public native void displayHelloJni();  
  3.     static {  
  4.         System.loadLibrary("helloJni");  
  5.     }  
  6.     public static void main(String[] args) {  
  7.         //System.out.println(System.getProperty("java.library.path"));  
  8.         new HelloJni().displayHelloJni();  
  9.     }  
  10. }  

在class檔案生成的相應目錄執行命令如下:
----------------------------------------------------
E:\projects\jni\target\classes>javah HelloJni
----------------------------------------------------

得到C++檔案HelloJni.h

Cpp程式碼  收藏程式碼
  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class HelloJni */  
  4. #ifndef _Included_HelloJni  
  5. #define _Included_HelloJni  
  6. #ifdef __cplusplus  
  7. extern "C" {  
  8. #endif  
  9. /* 
  10.  * Class:     HelloJni 
  11.  * Method:    displayHelloJni 
  12.  * Signature: ()V 
  13.  */  
  14. JNIEXPORT void JNICALL Java_HelloJni_displayHelloJni  
  15.   (JNIEnv *, jobject);  
  16. #ifdef __cplusplus  
  17. }  
  18. #endif  
  19. #endif  

JNI函式名稱分為三部分:首先是Java關鍵字,供Java虛擬機器識別;然後是呼叫者類名稱(全限定的類名,其中用下劃線代替名稱分隔符);最後是對應的方法名稱,各段名稱之間用下劃線分割。


JNI函式的引數也由三部分組成:首先是JNIEnv *,是一個指向JNI執行環境的指標;第二個引數隨本地方法是靜態還是非靜態而有所不同一一非靜態本地方法的第二個引數是對物件的引用,而靜態本地方法的第二個引數是對其Java類的引用;其餘的引數對應通常Java方法的引數,引數型別需要根據一定規則進行對映。

編寫C++檔案HelloJni.h的實現類,我是比較常用VC6.0來生成dll檔案(helloJni.dll)的

Cpp程式碼  收藏程式碼
  1. #include <jni.h>  
  2. #include "HelloJni.h"  
  3. #include <stdio.h>  
  4. JNIEXPORT void JNICALL   
  5. Java_HelloJni_displayHelloJni(JNIEnv *env, jobject obj)   
  6. {  
  7.     printf("Hello Dynamic Link Library has been calling!\n");  
  8.     printf("Java_HelloJni_displayHelloJni method has been executed!\n");  
  9.     return;  
  10. }  

其實此時,我們的工程目前還暫時不能生成我們想要的 helloJni.dll 檔案,問題就出在了“#include <jni.h>”。由於VC6.0裡沒有我們需要的“jni.h”檔案,因此就需要手動加入到VC6.0的環境中去。在JAVA_HOME路徑下我們可以找到include資料夾,其中就可以找到我們需要的“jni.h”檔案。為了避免以後麻煩起見,將所有的C++檔案全部拿出來,放在“%CPP_HOME%\VC98\Include”路徑下。然後將工程進行打包就可以得到我們需要的“helloJni.dll”檔案了。

 將helloJni.dll檔案放置於工程classes目錄,執行命令如下:
-----------------------------------------------
E:\projects\jni\target\classes>java HelloJni
-----------------------------------------------

執行結果如下:
-----------------------------------------------------------------
Hello Dynamic Link Library has been calling!
Java_HelloJni_displayHelloJni method has been executed!
-----------------------------------------------------------------


但是要想在eclipse中執行helloJni.dll檔案,就需要將檔案拷貝到工程的根目錄,或者將其放在諸如C:\WINDOWS\system32;C:\WINDOWS;等目錄下。因為,eclipse在執行helloJni.dll檔案時首先會去在當前根目錄找,如果找不到則在path上去找,因此你還可以為了方便管理生成的dll檔案,將所有工程中的dll檔案都放到一個特定的目錄,然後將該目錄加入到你的本地path環境變數中去,這樣每次只需要將生成的dll檔案放入path目錄下就可以訪問了。注,如果需要加環境變數最好在加好以後重新啟動一下eclipse,確保eclipse能夠載入到最新的path環境。

接下來,對小例子進行重構:
1. 新增一個基礎類

Java程式碼  收藏程式碼
  1. package org.danlley.jni.test;  
  2. public class BaseClass {  
  3.     public BaseClass(String arg) {  
  4.         loadLibrary(arg);  
  5.     }  
  6.     private static void loadLibrary(String arg) {  
  7.         System.loadLibrary(arg);  
  8.     }  
  9. }  

 2. 定義新類繼承基礎類

Java程式碼  收藏程式碼
  1. package org.danlley.jni.test;  
  2. public class HelloJniTest extends BaseClass {  
  3.     public HelloJniTest(String arg){  
  4.         super(arg);  
  5.     }  
  6.     public native void displayHelloJni();  
  7. }  

3. 編寫呼叫類

Java程式碼  收藏程式碼
  1. package org.danlley.jni.test;  
  2. public class RunMain {  
  3.     public static void main(String[] args) {  
  4.         new HelloJniTest("helloJniTest").displayHelloJni();  
  5.     }  
  6. }  

 此次,將dll檔案定義為:helloJniTest.dll。

執行結果:
------------------------------------------------------------------------------------
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
------------------------------------------------------------------------------------

例子相當簡單,沒有傳入引數,也沒有返回值,那麼是不是可以讓本地方法返回一些引數,同時又可以傳入資料進行處理,並把處理結果返回給方法的呼叫者呢,先拿基本型別開刀。接下來對 HelloJniTest 繼續進行改造:新增兩個本地方法,如下:

Java程式碼  收藏程式碼
  1. package org.danlley.jni.test;  
  2. public class HelloJniTest extends BaseClass {  
  3.     public HelloJniTest(String arg){  
  4.         super(arg);  
  5.     }  
  6.     public native void displayHelloJni();  
  7.     public native int getDynamicIntDataNoParam();  
  8.     public native int getDynamicIntData(int i);  
  9. }  

重新生成org_danlley_jni_test_HelloJniTest.h檔案,並改寫其實現類org_danlley_jni_test_HelloJniTest.cpp如下:

Cpp程式碼  收藏程式碼
  1. // org_danlley_jni_test_HelloJniTest.cpp: implementation of the org_danlley_jni_test_HelloJniTest class.  
  2. //  
  3. //////////////////////////////////////////////////////////////////////  
  4. #include "org_danlley_jni_test_HelloJniTest.h"  
  5. #include <jni.h>  
  6. #include <stdio.h>  
  7. JNIEXPORT void JNICALL   
  8. Java_org_danlley_jni_test_HelloJniTest_displayHelloJni(JNIEnv *env, jobject obj)   
  9. {  
  10.     printf("Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!\n");  
  11.     return;  
  12. }  
  13. JNIEXPORT jint JNICALL   
  14. Java_org_danlley_jni_test_HelloJniTest_getDynamicIntDataNoParam(JNIEnv *env, jobject obj)   
  15. {  
  16.     return 65535;  
  17. }  
  18. JNIEXPORT jint JNICALL   
  19. Java_org_danlley_jni_test_HelloJniTest_getDynamicIntData(JNIEnv *env, jobject obj, jint i)  
  20. {  
  21.     i*=i;  
  22.     return i;  
  23. }  

修改 RunMain 類:

Java程式碼  收藏程式碼
  1. package org.danlley.jni.test;  
  2. public class RunMain {  
  3.     public static void main(String[] args) {  
  4.         HelloJniTest tester=new HelloJniTest("helloJniTest");  
  5.         tester.displayHelloJni();  
  6.         int i=tester.getDynamicIntDataNoParam();  
  7.         System.out.println("tester.getDynamicIntDataNoParam()="+i);  
  8.         int j=tester.getDynamicIntData(100);  
  9.         System.out.println("tester.getDynamicIntData(100)="+j);  
  10.     }  
  11. }  

執行RunMain:

-----------------------------------------------------------------------
tester.getDynamicIntDataNoParam()=65535
tester.getDynamicIntData(100)=10000
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
-----------------------------------------------------------------------

OK,一切正常。

還是不過癮,簡單物件可以處理了,如果是一個java物件,還可以處理嗎,答案是當然可以,接下來我們來繼續對 helloJniTest 類進行改造。新增一個方法如下:

Java程式碼  收藏程式碼
  1. package org.danlley.jni.test;  
  2. public class HelloJniTest extends BaseClass {  
  3.     public HelloJniTest(String arg){  
  4.         super(arg);  
  5.     }  
  6.     public native void displayHelloJni();  
  7.     public native int getDynamicIntDataNoParam();  
  8.     public 

    相關推薦

    javaC/C++之間通過jni相互呼叫

    一、jni簡介: 企業應用 JNI一直以來都很少去關注,但卻是我心中的一個結,最近這幾天剛好手頭有點時間,因此抽空看了一下這方面的東西,整理了一份文件,JNI技術的出現主要是基於三個方面的應用需求: 1. 解決效能問題 Java具有平臺

    init.rc文件中面啟動c++程序,通過jni調用java實現

    mini val sni ril urn runtime sport mco env </pre><p>註:假設是自己的myself.jar包,還要修改例如以下:</p><p>target/product/core_bas

    C++項目通過JNI使用Java第三方jar包

    atoi home jdk版本 amd64 int 初始化 cat jvm rcp 最近在C++項目中碰到了需要使用第三方公司開發的Java jar包的問題,最後使用了JNI來解決。 參考了網絡上不少的方法介紹, 大多數介紹JNI的文章講的的都是Java通

    1分鐘瞭解JAVA大資料之間的關係

    JAVA和大資料是什麼關係大資料框架的編寫支援很多開發語言,但是Java在大資料開發方面有很大的優勢,目前流行的大資料Hadoop框架,很多部分都是用開源的Java語言編寫,因此Java在大資料方面有很大優勢。在大資料的中,也許別的你可能不在意,但是Hadoop想必你是注意到了的吧,大資料中不得不學的重要內容

    使用iperf測sta1h1的之間通過ap3的ap3-wlan1網絡卡的吞吐量

    ➜ demos git:(master) ✗ sudo python ./handover_line.py *** Creating nodes *** Configuring wifi nodes *** Associating and Creating l

    c++和c語言中的函式相互呼叫的問題

    1、如何C++程式要呼叫 已編譯後的C函式,該怎麼辦? (1)假設某個C函式的宣告如下: void foo(int x, int y); 該函式被C編譯器編譯後在庫中的名字為_foo,而C++編譯器則會產生像 _foo_int_int之類的名字用來支援過載和型別安全連線。由

    同一個C語言工程不同C檔案之間的函式互相呼叫問題(一)

    首先,新建一個頭檔案。例如,定義一個求和的功能的函式: function.h int add(int m, int n) { return m + n; } 然後,新建主函式main.c。 #include <stdio.h> #include "functi

    多個.C和.H檔案的相互呼叫

    假如  有一個頭檔案 1.h 和原始檔1.c       還有一個頭檔案 2.h和原始檔2.c 函式的呼叫 假如 有一個函式 在1.h 中宣告 void xuexi(void)                 在1.c中實現:先包含 #include "1.h"

    兩個Activity之間通過Intent相互(雙向)傳遞資料

    兩個Activity通過Intent相互交換資料,在Activity中用一個Button 選擇是否接受資料,不能直接寫在onCreate中否則在Activity1 中獲取從Activity2傳送的資料時會崩潰,原因是因為在Activity中會通過Intent再建立一個Act

    html5安卓native互動,相互呼叫

    轉發自http://blog.csdn.net/a38017032/article/details/47700325 WebView的使用 WebView是Android中的一個類,主要是用來承載網頁的資訊。WebView中比較重要的資訊有三個類:WebChromeC

    iframe框架之間js方法相互呼叫及資料傳遞

    <iframe id="gg" name="gg" src="gg.html" width="500" height="200"></iframe> <iframe id="dd" name="dd" src="dd.html" width="500" height="20

    兩個頁面之間js的相互呼叫

    一個頁面中需要另一個頁面的資料(選擇帶回),實現----> 頁面呼叫<span style="font-size:18px;"><a class="btnLook" href

    JavaC通過JNI指標相互傳遞

    轉載地址: http://blog.csdn.net/neo_86/article/details/24931509 注意 1、c中指標可以直接轉為java裡的int值,都是32位無損失(32位作業系統或者gcc 32編譯器)。 2、迴圈裡要注意釋放本地引用,因為迴圈太

    Android中JNI使用詳解(4)---JavaC之間資料型別轉換

    Jni中基本型別轉換對應的表格 Java型別 本地型別 說明 boolean jboolean 無符號,8位 byte jbyte

    NDK基礎(java ,c/c++, jni之間的關係及javac/c++之間相互呼叫

    1.java,c/c++,和jni之間的關係 java和c/c++可以相互呼叫,是因為java虛擬機器中的JNI。簡單的說就是用c/c++編寫一個動態連結庫讓Java虛擬機器去呼叫。(在windows環境下動態連結庫就是.dll檔案, 在Linux下就是.so檔案) 2.

    Android開發 通過JNI實現JAVAC/C++程式間的呼叫和回撥

           在一些Android應用的開發中,需要通過JNI和 Android NDK工具實現JAVA和C/C++之間的相互呼叫。        Java Native Interface (JNI)標準是java平臺的一部分,它允許Java程式碼和其他語言寫的程式碼進行

    Java小題,通過JNI調用本地C++共享庫中的對應方法實現楊輝三角的繪制

    question 文件夾 path ron variable iostream ring printf spl 1.在Eclipse中配置Javah,配置如下 位置是你javah.exe在你電腦磁盤上的路徑 位置:C:\Program Files\Java\jdk1.

    PythonC之間相互呼叫(Python C API及Python ctypes庫)

    分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

    Android JNIJavaC++物件如何建立一一對應的關係

    Android JNI中Java與C++物件如何建立一一對應的關係 環境 系統:Mac OSX IDE:android studio 3.0 1.JAVA物件持有C++物件的指標 在java類中建立long型別的變數如(long mNativeStudent )

    JAVA通過JNI呼叫C++動態連結庫CLL(一)

    簡介 本文筆者,詳細的演示JAVA通過JNI呼叫C++動態連結庫CLL的開發過程和涉及到知識點介紹,入門級簡單易懂 (一)Java本機介面(Java Native Interfa