原文:http://www.iteye.com/topic/295776
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小例子:
- public class HelloJni {
- public native void displayHelloJni();
- static {
- System.loadLibrary("helloJni");
- }
- public static void main(String[] args) {
- //System.out.println(System.getProperty("java.library.path"));
- new HelloJni().displayHelloJni();
- }
- }
在class文件生成的相應目錄執行命令如下:
----------------------------------------------------
E:\projects\jni\target\classes>javah HelloJni
----------------------------------------------------
得到C++文件HelloJni.h
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class HelloJni */
- #ifndef _Included_HelloJni
- #define _Included_HelloJni
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: HelloJni
- * Method: displayHelloJni
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_HelloJni_displayHelloJni
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
JNI函數名稱分為三部分:首先是Java關鍵字,供Java虛擬機識別;然后是調用者類名稱(全限定的類名,其中用下劃線代替名稱分隔符);最后是對應的方法名稱,各段名稱之間用下劃線分割。
JNI函數的參數也由三部分組成:首先是JNIEnv *,是一個指向JNI運行環境的指針;第二個參數隨本地方法是靜態還是非靜態而有所不同一一非靜態本地方法的第二個參數是對對象的引用,而靜態本地方法的第二個參數是對其Java類的引用;其余的參數對應通常Java方法的參數,參數類型需要根據一定規則進行映射。
編寫C++文件HelloJni.h的實現類,我是比較常用VC6.0來生成dll文件(helloJni.dll)的
- #include <jni.h>
- #include "HelloJni.h"
- #include <stdio.h>
- JNIEXPORT void JNICALL
- Java_HelloJni_displayHelloJni(JNIEnv *env, jobject obj)
- {
- printf("Hello Dynamic Link Library has been calling!\n");
- printf("Java_HelloJni_displayHelloJni method has been executed!\n");
- return;
- }
其實此時,我們的工程目前還暫時不能生成我們想要的 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. 新增一個基礎類
- package org.danlley.jni.test;
- public class BaseClass {
- public BaseClass(String arg) {
- loadLibrary(arg);
- }
- private static void loadLibrary(String arg) {
- System.loadLibrary(arg);
- }
- }
2. 定義新類繼承基礎類
- package org.danlley.jni.test;
- public class HelloJniTest extends BaseClass {
- public HelloJniTest(String arg){
- super(arg);
- }
- public native void displayHelloJni();
- }
3. 編寫調用類
- package org.danlley.jni.test;
- public class RunMain {
- public static void main(String[] args) {
- new HelloJniTest("helloJniTest").displayHelloJni();
- }
- }
此次,將dll文件定義為:helloJniTest.dll。
執行結果:
------------------------------------------------------------------------------------
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
------------------------------------------------------------------------------------
例子相當簡單,沒有傳入參數,也沒有返回值,那么是不是可以讓本地方法返回一些參數,同時又可以傳入數據進行處理,并把處理結果返回給方法的調用者呢,先拿基本類型開刀。接下來對 HelloJniTest 繼續進行改造:新增兩個本地方法,如下:
- package org.danlley.jni.test;
- public class HelloJniTest extends BaseClass {
- public HelloJniTest(String arg){
- super(arg);
- }
- public native void displayHelloJni();
- public native int getDynamicIntDataNoParam();
- public native int getDynamicIntData(int i);
- }
重新生成org_danlley_jni_test_HelloJniTest.h文件,并改寫其實現類org_danlley_jni_test_HelloJniTest.cpp如下:
- // org_danlley_jni_test_HelloJniTest.cpp: implementation of the org_danlley_jni_test_HelloJniTest class.
- //
- //////////////////////////////////////////////////////////////////////
- #include "org_danlley_jni_test_HelloJniTest.h"
- #include <jni.h>
- #include <stdio.h>
- JNIEXPORT void JNICALL
- Java_org_danlley_jni_test_HelloJniTest_displayHelloJni(JNIEnv *env, jobject obj)
- {
- printf("Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!\n");
- return;
- }
- JNIEXPORT jint JNICALL
- Java_org_danlley_jni_test_HelloJniTest_getDynamicIntDataNoParam(JNIEnv *env, jobject obj)
- {
- return 65535;
- }
- JNIEXPORT jint JNICALL
- Java_org_danlley_jni_test_HelloJniTest_getDynamicIntData(JNIEnv *env, jobject obj, jint i)
- {
- i*=i;
- return i;
- }
修改 RunMain 類:
- package org.danlley.jni.test;
- public class RunMain {
- public static void main(String[] args) {
- HelloJniTest tester=new HelloJniTest("helloJniTest");
- tester.displayHelloJni();
- int i=tester.getDynamicIntDataNoParam();
- System.out.println("tester.getDynamicIntDataNoParam()="+i);
- int j=tester.getDynamicIntData(100);
- System.out.println("tester.getDynamicIntData(100)="+j);
- }
- }
運行RunMain:
-----------------------------------------------------------------------
tester.getDynamicIntDataNoParam()=65535
tester.getDynamicIntData(100)=10000
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
-----------------------------------------------------------------------
OK,一切正常。
還是不過癮,簡單對象可以處理了,如果是一個java對象,還可以處理嗎,答案是當然可以,接下來我們來繼續對 helloJniTest 類進行改造。新增一個方法如下:
- package org.danlley.jni.test;
- public class HelloJniTest extends BaseClass {
- public HelloJniTest(String arg){
- super(arg);
- }
- public native void displayHelloJni();
- public native int getDynamicIntDataNoParam();
- public native int getDynamicIntData(int i);
- public native String getDynamicStringData(String arg);
- }
重新生成org_danlley_jni_test_HelloJniTest.h文件:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class org_danlley_jni_test_HelloJniTest */
- #ifndef _Included_org_danlley_jni_test_HelloJniTest
- #define _Included_org_danlley_jni_test_HelloJniTest
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: org_danlley_jni_test_HelloJniTest
- * Method: displayHelloJni
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_org_danlley_jni_test_HelloJniTest_displayHelloJni
- (JNIEnv *, jobject);
- /*
- * Class: org_danlley_jni_test_HelloJniTest
- * Method: getDynamicIntDataNoParam
- * Signature: ()I
- */
- JNIEXPORT jint JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicIntDataNoParam
- (JNIEnv *, jobject);
- /*
- * Class: org_danlley_jni_test_HelloJniTest
- * Method: getDynamicIntData
- * Signature: (I)I
- */
- JNIEXPORT jint JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicIntData
- (JNIEnv *, jobject, jint);
- /*
- * Class: org_danlley_jni_test_HelloJniTest
- * Method: getDynamicStringData
- * Signature: (Ljava/lang/String;)Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicStringData
- (JNIEnv *, jobject, jstring);
- #ifdef __cplusplus
- }
- #endif
- #endif
改寫org_danlley_jni_test_HelloJniTest.cpp文件:
- // org_danlley_jni_test_HelloJniTest.cpp: implementation of the org_danlley_jni_test_HelloJniTest class.
- //
- //////////////////////////////////////////////////////////////////////
- #include "org_danlley_jni_test_HelloJniTest.h"
- #include <jni.h>
- #include <stdio.h>
- JNIEXPORT void JNICALL
- Java_org_danlley_jni_test_HelloJniTest_displayHelloJni(JNIEnv *env, jobject obj)
- {
- printf("Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!\n");
- return;
- }
- JNIEXPORT jint JNICALL
- Java_org_danlley_jni_test_HelloJniTest_getDynamicIntDataNoParam(JNIEnv *env, jobject obj)
- {
- return 65535;
- }
- JNIEXPORT jint JNICALL
- Java_org_danlley_jni_test_HelloJniTest_getDynamicIntData(JNIEnv *env, jobject obj, jint i)
- {
- i*=i;
- return i;
- }
- JNIEXPORT jstring JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicStringData
- (JNIEnv *env, jobject obj, jstring arg){
- //Get the native string from javaString
- const char *nativeString = env->GetStringUTFChars(arg, 0);
- printf("%s", nativeString);
- //DON'T FORGET THIS LINE!!!
- env->ReleaseStringUTFChars(arg, nativeString);
- return arg;
- }
重新對C++工程打包成dll文件,運行結果:
---------------------------------------------------------------------------
tester.getDynamicIntDataNoParam()=65535
tester.getDynamicIntData(100)=10000
tester.getDynamicStringData=My first String test
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
My first String test
---------------------------------------------------------------------------
我們不僅把Java的一個String對象成功的傳給了dll,而且還將處理后的結果返回了出來。
但是總覺得還是不夠,那我們就再來個比較復雜的對象把,我們這次將一個整形數組通過java傳給dll,看看是不是也可以處理,繼續還是對 helloJniTest 類進行改造,新增一個方法:
- package org.danlley.jni.test;
- public class HelloJniTest extends BaseClass {
- public HelloJniTest(String arg){
- super(arg);
- }
- public native void displayHelloJni();
- public native int getDynamicIntDataNoParam();
- public native int getDynamicIntData(int i);
- public native String getDynamicStringData(String arg);
- public native int[] getDynamicArrayData(int[] args);
- }
重新生成org_danlley_jni_test_HelloJniTest.h文件
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class org_danlley_jni_test_HelloJniTest */
- #ifndef _Included_org_danlley_jni_test_HelloJniTest
- #define _Included_org_danlley_jni_test_HelloJniTest
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: org_danlley_jni_test_HelloJniTest
- * Method: displayHelloJni
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_org_danlley_jni_test_HelloJniTest_displayHelloJni
- (JNIEnv *, jobject);
- /*
- * Class: org_danlley_jni_test_HelloJniTest
- * Method: getDynamicIntDataNoParam
- * Signature: ()I
- */
- JNIEXPORT jint JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicIntDataNoParam
- (JNIEnv *, jobject);
- /*
- * Class: org_danlley_jni_test_HelloJniTest
- * Method: getDynamicIntData
- * Signature: (I)I
- */
- JNIEXPORT jint JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicIntData
- (JNIEnv *, jobject, jint);
- /*
- * Class: org_danlley_jni_test_HelloJniTest
- * Method: getDynamicStringData
- * Signature: (Ljava/lang/String;)Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicStringData
- (JNIEnv *, jobject, jstring);
- /*
- * Class: org_danlley_jni_test_HelloJniTest
- * Method: getDynamicArrayData
- * Signature: ([I)[I
- */
- JNIEXPORT jintArray JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicArrayData
- (JNIEnv *, jobject, jintArray);
- #ifdef __cplusplus
- }
- #endif
- #endif
改寫org_danlley_jni_test_HelloJniTest.cpp文件:
- // org_danlley_jni_test_HelloJniTest.cpp: implementation of the org_danlley_jni_test_HelloJniTest class.
- //
- //////////////////////////////////////////////////////////////////////
- #include "org_danlley_jni_test_HelloJniTest.h"
- #include <jni.h>
- #include <stdio.h>
- JNIEXPORT void JNICALL
- Java_org_danlley_jni_test_HelloJniTest_displayHelloJni(JNIEnv *env, jobject obj)
- {
- printf("Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!\n");
- return;
- }
- JNIEXPORT jint JNICALL
- Java_org_danlley_jni_test_HelloJniTest_getDynamicIntDataNoParam(JNIEnv *env, jobject obj)
- {
- return 65535;
- }
- JNIEXPORT jint JNICALL
- Java_org_danlley_jni_test_HelloJniTest_getDynamicIntData(JNIEnv *env, jobject obj, jint i)
- {
- i*=i;
- return i;
- }
- JNIEXPORT jstring JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicStringData
- (JNIEnv *env, jobject obj, jstring arg){
- //Get the native string from javaString
- const char *nativeString = env->GetStringUTFChars(arg, 0);
- printf("%s", nativeString);
- //DON'T FORGET THIS LINE!!!
- env->ReleaseStringUTFChars(arg, nativeString);
- return arg;
- }
- JNIEXPORT jintArray JNICALL Java_org_danlley_jni_test_HelloJniTest_getDynamicArrayData
- (JNIEnv *env, jobject obj, jintArray args){
- jint buf[10];
- jint i;
- env->GetIntArrayRegion(args, 0, 10, buf);
- jint j=0;
- for (i = 0; i < 10; i++) {
- j=buf[i];
- j*=j;
- buf[i]=j;
- }
- env->SetIntArrayRegion(args, 0, 10, buf);
- return args;
- }
改寫RunMain:
- package org.danlley.jni.test;
- public class RunMain {
- public static void main(String[] args) {
- HelloJniTest tester = new HelloJniTest("helloJniTest");
- tester.displayHelloJni();
- int i = tester.getDynamicIntDataNoParam();
- System.out.println("tester.getDynamicIntDataNoParam()=" + i);
- int j = tester.getDynamicIntData(100);
- System.out.println("tester.getDynamicIntData(100)=" + j);
- String str = tester.getDynamicStringData("My first String test");
- System.out.println("tester.getDynamicStringData=" + str);
- int[] args_int = new int[10];
- for (int ii = 0; ii < 10; ii++) {
- args_int[ii] = ii;
- }
- int[] args_arr = tester.getDynamicArrayData(args_int);
- for (int ii = 0; ii < 10; ii++) {
- System.out.println(args_arr[ii]);
- }
- }
- }
運行結果:
--------------------------------------------------------------------------------
tester.getDynamicIntDataNoParam()=65535
tester.getDynamicIntData(100)=10000
tester.getDynamicStringData=My first String test
0
1
4
9
16
25
36
49
64
81
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
My first String test
--------------------------------------------------------------------------------
參考資料:
http://en.wikipedia.org/wiki/Java_Native_Interface
http://www.yesky.com/20011004/199789.shtml
http://www.21ic.com/news/html/63/show13260.htm
http://java.ccidnet.com/art/297/20060228/439729_1.html
http://www.blogjava.net/soft/archive/2006/11/13/posoft.html
http://www.blogjava.net/soft/archive/2006/11/13/80788.html
http://www.blogjava.net/soft/archive/2006/11/13/80789.html
相關文章:http://wenku.baidu.com/link?url=cteRwimMeBG3l96CeQqgWxaJi04FXzdVu4wzv8QnvBH5NQHuHxM8WItbxIFU2ng-ybWtCN48JICsiEyILVHWCYxOdsjYZxF_ikgeNuMZR5G
文章列表