国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
JNI設(shè)計(jì)實(shí)踐之路
作者:楊小華
一、       前言
本文為在 32 位 Windows 平臺(tái)上實(shí)現(xiàn) Java 本地方法提供了實(shí)用的示例、步驟和準(zhǔn)則。本文中的示例使用 Sun公司的 Java Development Kit (JDK) 版本 1.4.2。 用 C ++語言編寫的本地代碼是用 Microsoft Visual C++  6.0編譯器編譯生成。規(guī)定在Java程序中function/method稱為方法,在C++程序中稱為函數(shù)。
本文將圍繞求圓面積逐步展開,探討java程序如何調(diào)用現(xiàn)有的DLL?如何在C++程序中創(chuàng)建,檢查及更新Java對(duì)象?如何在C++和Java程序中互拋異常,并進(jìn)行異常處理?最后將探討Eclipse及JBuilder工具可執(zhí)行文件為什么不到100K大小以及所采用的技術(shù)方案?
二、       JNI基礎(chǔ)知識(shí)簡介
Java語言及其標(biāo)準(zhǔn)API應(yīng)付應(yīng)用程序的編寫已綽綽有余。但在某些情況下,還是必須使用非Java代碼,例如:打印、圖像轉(zhuǎn)換、訪問硬件、訪問現(xiàn)有的非Java代碼等。與非Java代碼的溝通要求獲得編譯器和JVM的專門支持,并需附加的工具將Java代碼映射成非Java代碼。目前,不同的開發(fā)商為我們提供了不同的方案,主要有以下方法:
1.         JNI(Java Native Interface)
2.         JRI(Java Runtime Interface)
3.         J/Direct
4.         RNI(Raw Native Interface)
5.         Java/COM集成方案
6.         CORBA(Common Object Request Broker Architecture)
其中方案1是JDK自帶的一部分,方案2由網(wǎng)景公司所提供,方案3 、 4 、 5是微軟所提供的方案,方案6是一家非盈利組織開發(fā)的一種集成技術(shù),利用它可以在由不同語言實(shí)現(xiàn)的對(duì)象之間進(jìn)行“相互操作”的能力。
在開發(fā)過程中,我們一般采用第1種方案――JNI技術(shù)。因?yàn)橹挥卯?dāng)程序用Microsoft Java編譯器編譯,而且只有在Microsoft Java虛擬機(jī)(JVM)上運(yùn)行的時(shí)候,才采用方案3 、 4 、 5。而方案6一般應(yīng)用在大型的分布式應(yīng)用中。
JNI是一種包容極廣的編程接口,允許我們從Java應(yīng)用程序里調(diào)用本地化方法。也就是說,JNI允許運(yùn)行在虛擬機(jī)上的Java程序能夠與其它語言(例如C/ C++/匯編語言)編寫的程序或者類庫進(jìn)行相互間的調(diào)用。同時(shí)JNI也提供了一整套的API,允許將Java虛擬機(jī)直接嵌入到本地的應(yīng)用程序中。其中JNI所扮演的角色可用圖一描述:
圖一 JNI基本結(jié)構(gòu)描述圖
目前JNI只能與用C和C++編寫的本地化方法打交道。利用JNI,我們本地化方法可以:
1.         創(chuàng)建、檢查及更新Java對(duì)象
2.         調(diào)用Java和非Java程序所編寫的方法(函數(shù)),以及win32 API等.
3.         捕獲和拋出“異?!?div style="height:15px;">
4.         裝載類并獲取類信息
5.         進(jìn)行運(yùn)行期類型檢查
所以,原來在Java程序中能對(duì)類及對(duì)象所做的幾乎所有事情都可以在本地化方法中實(shí)現(xiàn)。
下圖表示了通過JNI,Java程序和非Java程序相互調(diào)用原理。
圖二   Java程序和非Java程序通過JNI相互調(diào)用原理
通過JNI,編寫Java程序調(diào)用非Java程序一般步驟:
1.)    編寫對(duì)本地化方法及自變量進(jìn)行聲明的Java代碼
2.)    利用頭文件生成器javah生成本地化方法對(duì)應(yīng)的頭文件
3.)    利用C和C++實(shí)現(xiàn)本地化方法(可調(diào)用非Java程序),并編譯、鏈接生成DLL文件
4.)    Java程序通過生成的DLL調(diào)用非Java程序
同時(shí)我們也可以通過JNI,將Java虛擬機(jī)直接嵌入到本地的應(yīng)用程序中,步驟很簡單,只需要在C/C++程序中以JNI API函數(shù)為媒介調(diào)用Java程序。
以上步驟雖簡單,但有很多地方值得注意。如果一招不慎,可能造成滿盤皆輸。
三、       Java程序調(diào)用非Java程序
3.1 本地化方法聲明及頭文件生成
任務(wù):現(xiàn)有一求圓面積的Circle.dll(用MFC編寫,參數(shù):圓半徑返回值:圓面積)文件,在Java程序中調(diào)用該Dll。
在本地化聲明中,可分無包和有包兩種情況。我們主要對(duì)有包的情況展開討論。
實(shí)例1:
package com.testJni;
public class Circle
{
public native void cAreas(int radius) ;
static
{
//System.out.println(System.getProperty("java.library.path"));
System.loadLibrary("CCircle");
}
}
在Java程序中,需要在類中聲明所調(diào)用的庫名稱System.loadLibrary( String libname );
該函數(shù)是將一個(gè)Dll/so庫載入內(nèi)存,并建立同它的鏈接。定位庫的操作依賴于具體的操作系統(tǒng)。在windows下,首先從當(dāng)前目錄查找,然后再搜尋”PATH”環(huán)境變量列出的目錄。如果找不到該庫,則會(huì)拋出異常UnsatisfiedLinkError。庫的擴(kuò)展名可以不用寫出來,究竟是Dll還是so,由系統(tǒng)自己判斷。這里加載的是3.2中生成的DLL,而不是其他應(yīng)用程序生成的Dll。還需要對(duì)將要調(diào)用的方法做本地聲明,關(guān)鍵字為native。表明此方法在本地方法中實(shí)現(xiàn),而不是在Java程序中,有點(diǎn)類似于關(guān)鍵字abstract。
我們寫一個(gè)Circle.bat批處理文件編譯Circle.java文件,內(nèi)容如下(可以用其他工具編譯):
javac -d . Circle.java
javah com.testJni.Circle
pause
對(duì)于有包的情況一定要注意這一點(diǎn),就是在用javah時(shí)有所不同。開始時(shí)我的程序始終運(yùn)行都不成功,問題就出在這里。本類名稱的前面均是包名。這樣生成的頭文件就是:com_testJni_Circle.h。開始時(shí),在包含包的情況下我用javah Circle生成的頭文件始終是Circle.h。在網(wǎng)上查資料時(shí),看見別人的頭文件名砸那長,我的那短。但不知道為什么,現(xiàn)在大家和我一樣知道為什么了吧。:)。
如果是無包的情況,則將批處理文件換成如下內(nèi)容:
javac Circle.java
javah Circle
pause
3.2 本地化方法實(shí)現(xiàn)
剛才生成的com_testJni_Circle.h頭文件內(nèi)容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_testJni_Circle */
#ifndef _Included_com_testJni_Circle
#define _Included_com_testJni_Circle
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_testJni_Circle
* Method:    cAreas
* Signature:  (I)V
*/
JNIEXPORT void JNICALL Java_com_testJni_Circle_cAreas
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
如果在本地化方法聲明中,方法cAreas ()聲明為static類型,則與之相對(duì)應(yīng)的Java_com_testJni_Circle_cAreas()函數(shù)中的第二個(gè)參數(shù)類型為jclass。也就是
JNIEXPORT void JNICALL Java_com_testJni_Circle_cAreas(JNIEnv *env, jclass newCircle,jint radius)。
這里JNIEXPORT和JNICALL都是JNI的關(guān)鍵字,其實(shí)是一些宏(具體參看jni_md.h文件)。
從以上頭文件中,可以看出函數(shù)名生成規(guī)則為:Java[ _包名]_類名_方法名[ _函數(shù)簽名](其中[ ]是可選項(xiàng)),均以字符下劃線( _ )分割。如果是無包的情況,則不包含[ _包名]選項(xiàng)。如果本地化方法中有方法重載,則在該函數(shù)名最后面追加函數(shù)簽名,也就是Signature對(duì)應(yīng)的值,函數(shù)簽名參見表一。
函數(shù)簽名
Java類型
V
void
Z
boolean
B
byte
C
char
S
short
I
int
J
long
F
float
D
double
L fully-qualified-class ;
fully-qualified-class
[ type
type[]
( arg-types ) ret-type
method type
表一函數(shù)簽名與Java類型的映射
在具體實(shí)現(xiàn)的時(shí)候,我們只關(guān)心函數(shù)原型:
JNIEXPORT void JNICALL Java_com_testJni_Circle_cAreas(JNIEnv *, jobject, jint);
現(xiàn)在就讓我們開始激動(dòng)人心的一步吧 : ) 。啟動(dòng)VC集成開發(fā)環(huán)境,新建一工程,在project里選擇win32 Dynamic-link Library,輸入工程名,然后點(diǎn)擊ok,接下去步驟均取默認(rèn)(圖三)。如果不取默認(rèn),生成的工程將會(huì)有DllMain ()函數(shù),反之將無這個(gè)函數(shù)。我在這里取的是空。
圖三   新建DLL工程
然后選擇菜單File->new->Files->C++ Source File,生成一個(gè)空*.cpp文件,取名為CCircle。與3.1中System.loadLibrary("CCircle");參數(shù)保持一致。將JNIEXPORT void JNICALL Java_com_testJni_Circle_cAreas(JNIEnv *, jobject, jint);拷貝到CPP文件中,并包含其頭文件。
對(duì)應(yīng)的CCircle.cpp內(nèi)容如下:
#include<iostream.h>
#include"com_testJni_Circle.h"
#include"windows.h"
JNIEXPORT void JNICALL Java_com_testJni_Circle_cAreas(JNIEnv *env, jobject newCircle,jint radius)
{
//調(diào)用求圓面積的Circle.dll
typedef void (*PCircle)(int radius);
HINSTANCE hDLL;
PCircle Circle;
hDLL=LoadLibrary("Circle.dll");//加載動(dòng)態(tài)鏈接庫Circle.dll文件
Circle=(PCircle)GetProcAddress(hDLL,"Circle");
Circle(8);
FreeLibrary(hDLL);//卸載Circle.dll文件;
}
在編譯前一定要注意下列情況。
注意:一定要把SDK目錄下include文件夾及其下面的win32文件夾中的頭文件拷貝到VC目錄的include文件夾下?;蛘咴赩C的tools\options\directories中設(shè)置,如圖四所示。
圖四   頭文件設(shè)置
我們知道dll文件有兩種指明導(dǎo)出函數(shù)的方法,一種是在.def文件中定義,另一種是在定義函數(shù)時(shí)使用關(guān)鍵字__declspec(dllexport)。而關(guān)鍵字JNIEXPORT實(shí)際在jni_md.h中如下定義,#define JNIEXPORT __declspec(dllexport),可見JNI默認(rèn)的導(dǎo)出函數(shù)使用第二種。使用第二種方式產(chǎn)生的導(dǎo)出函數(shù)名會(huì)根據(jù)編譯器發(fā)生變化,在有的情況下會(huì)發(fā)生找不到導(dǎo)出函數(shù)的問題(我們?cè)趈ava控制臺(tái)程序中調(diào)用很正常,但把它移植到JSP頁面時(shí),就發(fā)生了該問題,JVM開始崩潰,百思不得其解,后來加入一個(gè).def文件才解決問題)。其實(shí)在《windows 核心編程》一書中,第19.3.2節(jié)就明確指出創(chuàng)建用于非Visual C++工具的DLL時(shí),建議加入一個(gè)def文件,告訴Microsoft編譯器輸出沒有經(jīng)過改變的函數(shù)名。因此最好采用第一種方法,定義一個(gè).def文件來指明導(dǎo)出函數(shù)。本例中可以新建一個(gè)CCircle.def文件,內(nèi)容如下:
; CCircle.def : Declares the module parameters for the DLL.
LIBRARY      "CCircle"
DESCRIPTION 'CCircle Windows Dynamic Link Library'
EXPORTS
; Explicit exports can go here
Java_com_testJni_Circle_cAreas
現(xiàn)在開始對(duì)所寫的程序進(jìn)行編譯。選擇build->rebuild all對(duì)所寫的程序進(jìn)行編譯。點(diǎn)擊build->build CCirclee.DLL生成DLL文件。
也可以用命令行cl來編譯。語法格式參見JDK文檔JNI部分。再次強(qiáng)調(diào)(曾經(jīng)為這個(gè)東西大傷腦筋):DLL放置地方
1)        當(dāng)前目錄。
2)        Windows的系統(tǒng)目錄及Windows目錄
3)        放在path所指的路徑中
4)        自己在path環(huán)境變量中設(shè)置一個(gè)路徑,要注意所指引的路徑應(yīng)該到.dll文件的上一級(jí),如果指到.dll,則會(huì)報(bào)錯(cuò)。
下面就開始測試我們的所寫的DLL吧(假設(shè)DLL已放置正確)。
import com.testJni.Circle;
public class test
{
public static void main(String argvs[])
{
Circle myCircle;
myCircle = new Circle();
myCircle.cAreas(2);
}
}
編譯,運(yùn)行程序,將會(huì)彈出如下界面:
圖五 運(yùn)行結(jié)果
以上是我們通過JNI方法調(diào)用的一個(gè)簡單程序。實(shí)際情況要比這復(fù)雜的多。
現(xiàn)在開始來討論JNI中參數(shù)的情況,我們來看一個(gè)程序片斷。
實(shí)例二:
JNIEXPORT jstring JNICALL Java_MyNative_cToJava
(JNIEnv *env, jclass obj)
{
jstring jstr;
char str[]="Hello,word!\n";
jstr=env->NewStringUTF(str);
return jstr;
}
在C和Java編程語言之間傳送值時(shí),需要理解這些值類型在這兩種語言間的對(duì)應(yīng)關(guān)系。這些都在頭文件jni.h中定義,用typedef語句聲明了這些類在目標(biāo)平臺(tái)上的代價(jià)類。頭文件也定義了常量如:JNI_FALSE=0 和JNI_TRUE=1;表二和表三說明了Java類型和C類型之間的映射關(guān)系。
Java語言
C/C++語言
bit位數(shù)
boolean
jboolean
8 unsigned
byte
jbyte
8
char
jchar
16 unsigned
short
jshort
16
int
jint
32
long
jlong
64
float
jfloat
32
double
jdouble
64
void
void
0
表二   Java基本類型到本地類型的映射
表三 Java中的類到本地類的映射
JNI函數(shù)NewStringUTF()是從一個(gè)包含UTF格式編碼字符的char類型數(shù)組中創(chuàng)建一個(gè)新的jstring對(duì)象。jstring是以JNI為中介使Java的String類型與本地的string溝通的一種類型,我們可以視而不見 (具體對(duì)應(yīng)見表二和表三)。如果你使用的函數(shù)是GetStringUTFChars()(將jstring轉(zhuǎn)換為UTF-8字符串),必須同時(shí)使用ReleaseStringUTFChars()函數(shù),通過它來通知虛擬機(jī)去回收UTF-8串占用的內(nèi)存,否則將會(huì)造成內(nèi)存泄漏,最終導(dǎo)致系統(tǒng)崩潰。因?yàn)镴VM在調(diào)用本地方法時(shí),是在虛擬機(jī)中開辟了一塊本地方法棧供本地方法使用,當(dāng)本地方法使用完UTF-8串后,得釋放所占用的內(nèi)存。其中程序片斷jstr=env->NewStringUTF(str);是C++中的寫法,不必使用env指針。因?yàn)镴NIEnv函數(shù)的C++版本包含有直接插入成員函數(shù),他們負(fù)責(zé)查找函數(shù)指針。而對(duì)于C的寫法,應(yīng)改為:jstr=(*env)->NewStringUTF(env,str);因?yàn)樗蠮NI函數(shù)的調(diào)用都使用env指針,它是任意一個(gè)本地方法的第一個(gè)參數(shù)。env指針是指向一個(gè)函數(shù)指針表的指針。因此在每個(gè)JNI函數(shù)訪問前加前綴(*env)->,以確保間接引用函數(shù)指針。
C/C++和Java互傳參數(shù)需要自己在編程過程中仔細(xì)摸索與體味。
四、       C/C++訪問Java成員變量和成員方法
我們修改3.1中的Java程序聲明,加入如下代碼:
private int circleRadius;
public Circle()
{
circleRadius=0;
}
public void setCircleRadius(int radius)
{
circleRadius=radius;
}
public void javaAreas()
{
float PI = 3.14f;
if(circleRadius<=0)
{
System.out.println (“error!”);
}
else
{
System.out.println (PI*circleRadius*circleRadius);
}
}
在C++程序中訪問Circle類中的private私有成員變量circleRadius,并設(shè)置它的值,同時(shí)調(diào)用Java方法javaAreas()。在函數(shù)Java_com_testJni_Circle_cAreas()中加入如下代碼:
jclass circle;
jmethodID AreasID;
jfieldID radiusID;
jint newRadius=5;
circle = env->GetObjectClass(newCircle);//get current class
radiusID=env->GetFieldID(circle,"circleRadius","I");//get field ID
env->SetIntField(newCircle,radiusID,newRadius);//set field value
AreasID=env->GetMethodID(circle,"javaAreas","()V");//get method ID
env->CallVoidMethod(newCircle,AreasID,NULL);//invoking method
在C++代碼中,創(chuàng)建、檢查及更新Java對(duì)象,首先要得到該類,然后再根據(jù)類得到其成員的ID,最后根據(jù)該類的對(duì)象,ID號(hào)調(diào)用成員變量或者成員方法。
得到類,有兩個(gè)API函數(shù),分別為FindClass()和GetObjectClass();后者顧名思義用于已經(jīng)明確知道其對(duì)象,然后根據(jù)對(duì)象找類。前者用于得到?jīng)]有實(shí)例對(duì)象的類。這里也可以改成circle = env-> FidnClass("com/testJni/Circle");其中包的分隔符用字符" /"代替。如果已知一個(gè)類,也可以在C++代碼中創(chuàng)建該類對(duì)象,其JNI函數(shù)為NewObject();示例代碼如下:
jclass      circle =env->FindClass("com/testJni/ Circle ");
jmethodID circleID=env->GetMethodID(circle,"<init>","()V");//得到構(gòu)造函數(shù)的ID
jobject newException=env->NewObject(circle, circleID,NULL);
得到成員變量的ID,根據(jù)其在Java代碼中聲明的類型不同而不同。具體分為兩大類:非static型和static型,分別對(duì)應(yīng)GetFieldID()和GetStaticFieldID()。同時(shí)也可以獲得和設(shè)置成員變量的值,根據(jù)其聲明的type而變化,獲得其值的API函數(shù)為:GettypeField()和GetStatictypeField();與之相對(duì)應(yīng)的設(shè)置其值的函數(shù)為SettypeField()和SetStatictypeField();在本例中,成員變量circleRadius聲明成int型,則對(duì)應(yīng)的函數(shù)分別為GetIntField()和SetIntField();
其實(shí)JNI API函數(shù)名是很有規(guī)律的,從上面已窺全貌。獲得成員方法的ID也是同樣的分類方法。具體為GetMethodID()和GetStaticMethodID()。調(diào)用成員方法跟獲得成員變量的值相類似,也根據(jù)其方法返回值的type不同而不同,分別為CalltypeMethod()和CallStatictypeMethod()。對(duì)于返回值為void的類型,其相應(yīng)JNI函數(shù)為CallVoidMethod();
以上獲得成員ID函數(shù)的形參均一致。第一個(gè)參數(shù)為jclass,第二個(gè)參數(shù)為成員變量或方法,第三個(gè)參數(shù)為該成員的簽名(簽名可參見表一)。但調(diào)用或設(shè)置成員變量或方法時(shí),第一個(gè)參數(shù)為實(shí)例對(duì)象(即jobject),其余形參與上面相同。
特別要注意的是得到構(gòu)造方法的ID時(shí),第二個(gè)參數(shù)不遵循上面的原則,為jmethodID constructorID = env->GetMethodID(jclass, "<init>"," 函數(shù)簽名");
從上面代碼中可以看出,在C++中可以訪問java程序private類型的變量,嚴(yán)重破壞了類的封裝原則。從而可以看出其不安全性。
五、       異常處理
本地化方法穩(wěn)定性非常差,調(diào)用任何一個(gè)JNI函數(shù)都會(huì)出錯(cuò),為了程序的健壯性,非常有必要在本地化方法中加入異常處理。我們繼續(xù)修改上面的類。
我們聲明一個(gè)異常類,其代碼如下:
package com.testJni;
import com.testJni.*;
public class RadiusIllegal extends Exception
{
protected String MSG="error!";
public RadiusIllegal(String message)
{
MSG=message;
}
public void print()
{
System.out.println(MSG);
}
}
同時(shí)也修改Circle.java中的方法,加入異常處理。
public void javaAreas() throws RadiusIllegal    //修改javaAreas(),加入異常處理
{
float PI = 3.14f;
if(circleRadius<=0)
{
throw new RadiusIllegal("warning:radius is illegal!");
}
else
{
System.out.println (PI*circleRadius*circleRadius);
}
}
public native void cAreas(int radius) throws RadiusIllegal;    //修改cAreas (),加入異常處理
修改C++代碼中的函數(shù),加入異常處理,實(shí)現(xiàn)Java和C++互拋異常,并進(jìn)行異常處理。
JNIEXPORT void JNICALL Java_com_testJni_Circle_cAreas(JNIEnv *env, jobject newCircle,jint radius)
{
//此處省略部分代碼
radiusIllegal=env->FindClass("com/testJni/RadiusIllegal");//get the exception class
if((exception=env->ExceptionOccurred())!=NULL)
{
cout<<"errors in com_testJni_RadiusIllegal"<<endl;
env->ExceptionClear();
}
//此處省略部分代碼
env->CallVoidMethod(newCircle,AreasID,NULL);//invoking
if((exception=env->ExceptionOccurred())!=NULL)
{
if(env->IsInstanceOf(exception,radiusIllegal)==JNI_TRUE)
{
cout<<"errors in java method"<<endl;
env->ExceptionClear();
}
else
{
cout<<"errors in invoking javaAreas() method of Circle"<<endl;
env->ExceptionClear();
}
}
if(radius<=0)
{
env->ThrowNew(radiusIllegal,"errors in C function!");//throw exception
return ;
}
else
{
//此處為調(diào)用計(jì)算圓面積的DLL
}
}
在本地化方法(C++)中,可以自己處理異常,也可以重新拋出異常,讓Java程序來捕獲該異常,進(jìn)行相關(guān)處理。
如果調(diào)用JNI函數(shù)發(fā)生異常,不及時(shí)進(jìn)行處理,再次調(diào)用其他JNI函數(shù)時(shí),可能會(huì)使JVM崩潰(crash),
大多數(shù)JNI函數(shù)都具有此特性??梢哉{(diào)用函數(shù)ExceptionOccurred()來判斷是否發(fā)生了異常。該函數(shù)返回jthrowable的實(shí)例對(duì)象,如本例if((exception=env->ExceptionOccurred())!=NULL)就用來判斷是否發(fā)生了異常。當(dāng)要判斷具體是哪個(gè)異常發(fā)生時(shí),可以用IsInstanceOf()來進(jìn)行測試,此函數(shù)非彼IsInstanceOf(Java語言中的IsInstanceOf)。在上面的代碼中,我們?cè)诒镜鼗椒ㄖ薪ocircleRadius設(shè)置了一非法值,然后調(diào)用方法javaAreas(),此時(shí)java代碼會(huì)拋出異常,在本地化方法中進(jìn)行捕獲,然后用IsInstanceOf()來進(jìn)行測試是否發(fā)生了RadiusIllegal類型的異常,以便進(jìn)行相關(guān)處理。在調(diào)用其他JNI函數(shù)之前,應(yīng)當(dāng)首先清除異常,其函數(shù)為ExceptionClear()。
如果是C++的程序發(fā)生異常,則可以用JNI API函數(shù)ThrowNew()拋出該異常。但此時(shí)本地化方法并不返回退出,直到該程序執(zhí)行完畢。所以當(dāng)在本地化方法中發(fā)生異常時(shí),應(yīng)該人為的退出,及時(shí)進(jìn)行處理,避免程序崩潰。函數(shù)ThrowNew()中第一個(gè)參數(shù)為jclass的類,第二個(gè)參數(shù)為附加信息,用來描述異常信息。
如果要知道異常發(fā)生的詳細(xì)信息,或者對(duì)程序進(jìn)行調(diào)試時(shí),可以用函數(shù)ExceptionDescribe()來顯示異常棧里面的內(nèi)容。
六、       MFC程序中嵌入Java虛擬機(jī)
可能大家天天都在用Eclipse和Jbulider這兩款優(yōu)秀的IDE進(jìn)行程序開發(fā),可能還不知道他們的可執(zhí)行文件不到100KB大小,甚則連一副圖片都可能比他們大。其實(shí)隱藏在他們背后的技術(shù)是JNI,可執(zhí)行文件只是去啟動(dòng)Java程序,所以也只有那么小。
我們只需要在MFC程序中創(chuàng)建一個(gè)JVM,然后基于這個(gè)JVM調(diào)用Java的方法,啟動(dòng)Java程序,就可以模擬出Eclipse和Jbulider的那種效果,使java程序更專業(yè)。其實(shí)要實(shí)現(xiàn)這種效果,用上面的方法足有夠有。創(chuàng)建JVM,只需包含相應(yīng)的類庫,設(shè)置相關(guān)的屬性。
首先進(jìn)行環(huán)境設(shè)置,在VC環(huán)境的tools-->options-->Directories下的Library files選項(xiàng)中包含其創(chuàng)建JVM的庫文件jvm.lib,該庫文件位于JDK \ lib目錄下,如圖6所示:
圖六庫文件路徑設(shè)置
然后,在環(huán)境變量path中設(shè)置jvm.dll的路徑。該Dll      位于jdk\jre\bin\server目錄或jdk\jre\bin\client目錄下。注意:一定不要將jvm.dll和jvm.lib拷貝到你應(yīng)用程序路徑下,這樣會(huì)引起JVM初始化失敗。因?yàn)镴ava虛擬機(jī)是以相對(duì)路徑來尋找和調(diào)用用到的庫文件和其他相關(guān)文件。
接下來,我們?cè)贛FC程序(該程序請(qǐng)到《程序員》雜志頻道下載)中進(jìn)行創(chuàng)建JVM初始化工作。示例代碼如下:
JNIEnv *env;
JavaVM *jvm;
jint res;
jclass cls;
jmethodID mid;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
memset(&vm_args, 0, sizeof(vm_args));
//進(jìn)行初始化工作
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.";
options[2].optionString = "-verbose:jni";
vm_args.version=JNI_VERSION_1_4;       //版本號(hào)設(shè)置
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm,(void**)&env,&vm_args); //創(chuàng)建JVM
if (res < 0)
{
MessageBox( "Can't create Java VM","Error",MB_OK|MB_ICONERROR);
exit(1);
}
cls = env->FindClass("prog");
if(env->ExceptionOccurred()!=NULL)
{
MessageBox( "Can't find Prog class!","Error",MB_OK|MB_ICONERROR);
exit(1);
}
mid = env->GetStaticMethodID(cls, "main", "([Ljava/lang/String;)V");
if(env->ExceptionOccurred()!=NULL)
{
MessageBox("Can't find Prog.main!","Error",MB_OK|MB_ICONERROR);
exit(1);
}
env->CallStaticVoidMethod( cls, mid, NULL); //調(diào)用Java程序main()方法,啟動(dòng)Java程序
if(env->ExceptionOccurred()!=NULL)
{
MessageBox( "Fatal Error!","Error",MB_OK|MB_ICONERROR);
exit(1);
}
jvm->DestroyJavaVM();//釋放JVM資源
程序首先進(jìn)行JVM初始化設(shè)置。我們觀察jni.h 文件關(guān)于JavaVMOption和JavaVMInitArgs的定義
typedef struct JavaVMOption {
char *optionString;
void *extraInfo;
} JavaVMOption;
typedef struct JavaVMInitArgs {
jint version;
jint nOptions;
JavaVMOption *options;
jboolean ignoreUnrecognized;
} JavaVMInitArgs;
結(jié)構(gòu)體JavaVMInitArgs中有四個(gè)參數(shù),我們?cè)诔绦蛑卸嫉帽仨氃O(shè)置。其中版本號(hào)一定要設(shè)置正確,不同的版本有不同的設(shè)置方法,關(guān)于版本1.1和1.2的設(shè)置方法參看sun公司的文檔,這里只給出版本1.4的設(shè)置方法。第二個(gè)參數(shù)表示JavaVMOption結(jié)構(gòu)體變量的維數(shù),這里設(shè)置為三維,其中options[0].optionString = "-Djava.compiler=NONE";表示disable JIT;options[1].optionString = "-Djava.class.path=.";表示你所調(diào)用Java程序的Class文件的路徑,這里設(shè)置為該exe應(yīng)用程序的根路徑(最后一個(gè)字符"."表示根路徑);options[2].optionString = "-verbose:jni";用于跟蹤運(yùn)行時(shí)的信息。第三個(gè)參數(shù)是一個(gè)JavaVMOption的指針變量。第四個(gè)參數(shù)意思我們可以參看幫助文檔的解釋If ignoreUnrecognized is JNI_FALSE, JNI_CreateJavaVM returns JNI_ERR as soon as it encounters any unrecognized option strings。
初始化完畢后,就可以調(diào)用創(chuàng)建JVM的函數(shù)jint JNICALL JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args);如果返回值小于0表示創(chuàng)建JVM失敗。最可能的原因就是jvm.dll和jvm.lib設(shè)置錯(cuò)誤。
如果在運(yùn)行的過程中找不到j(luò)ava程序的類,那么就是-Djava.class.path設(shè)置錯(cuò)誤。只要JVM創(chuàng)建成功,就可以根據(jù)上面的方法調(diào)用java程序。最后當(dāng)程序結(jié)束后,調(diào)用函數(shù)DestroyJavaVM()摧毀JVM,釋放資源。
七、       附錄
利用JNI函數(shù),我們可以從本地化方法的內(nèi)部與JVM打交道。正如在前面的例子中所看到的那樣,每個(gè)本地化方法中都會(huì)接收一個(gè)特殊的自變量作為自己的第一個(gè)參數(shù):JNIEnv――它是指向類型為JNIEnv_的一個(gè)特殊JNI數(shù)據(jù)結(jié)構(gòu)的指針。JNI數(shù)據(jù)結(jié)構(gòu)的一個(gè)元素是指向由JVM生成的一個(gè)指針的數(shù)組;該數(shù)組的每個(gè)元素都是指向一個(gè)JNI函數(shù)的指針??梢詮谋镜鼗椒ǖ膬?nèi)部對(duì)JNI函數(shù)的調(diào)用。第二個(gè)參數(shù)會(huì)根據(jù)Java類中本地方法的定義不同而不同,如果是定義為static方法,類型會(huì)是jclass,表示對(duì)特定Class對(duì)象的引用,如果是非static方法,類型是jobject,表示當(dāng)前對(duì)象的引用,相當(dāng)于” this”??梢哉f這兩個(gè)變量是本地化方法返回JAVA的大門。
注意:在本地化方法中生成的Dll不具備到處運(yùn)行的特性,而具有”牽一發(fā)而動(dòng)全身”的特點(diǎn)。只要包名一改變,那么你所有的工作就得重新做一遍。原因就是當(dāng)用javah生成頭文件時(shí),函數(shù)名的生成規(guī)則為Java[ _包名]_類名_方法名[ _函數(shù)簽名];當(dāng)你的包名改變時(shí),生成的函數(shù)名也跟著改變了,那么你再次調(diào)用以前編寫的Dll時(shí),會(huì)拋出異常。
八、       參考文獻(xiàn)
1.         《Java 編程思想》Bruce Eckel 機(jī)械工業(yè)出版社
2.         《Java2 核心技術(shù)卷2》(第6版)Cay S.Horstmann,Gary Cornell機(jī)械工業(yè)出版社
3.         《高級(jí)Java2 大學(xué)教程》(英文版) Harvey M.Deitel ,Paul J.Deitel,Sean E.Santry 電子工業(yè)出版社
4.         《windows 核心編程》Jeffrey Richter 機(jī)械工業(yè)出版社
5.           http://www.jguru.com
6.                    sun公司文檔
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
JNI技術(shù)實(shí)踐小結(jié)--原理分析和詳細(xì)步驟截圖說明 - - Java - JavaEye論壇
JNI介紹
Java調(diào)用DLL動(dòng)態(tài)鏈接庫
JAVA調(diào)用dll方法
Android中JNI編程的那些事兒 - ------------------------...
JNI官方規(guī)范中文版——如何把一個(gè)JVM嵌入到本地程序中
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服