<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    談笑有鴻儒,往來無白丁

    在恰當(dāng)?shù)臅r間、地點以恰當(dāng)?shù)姆绞奖磉_(dá)給恰當(dāng)?shù)娜?..  閱讀的時候請注意分類,佛曰我日里面是談笑文章,其他是各個分類的文章,積極的熱情投入到寫博的隊伍中來,支持blogjava做大做強(qiáng)!向dudu站長致敬>> > 我的微博敬請收聽

    JNI(Java Native Interface Java 本地接口 ) 技術(shù)大家都不陌生,它可以幫助解決 Java 訪問底層硬件的局限和執(zhí)行效率的提高。關(guān)于 JNI 的開發(fā),大多數(shù)資料討論的都是如何用 C/C++ 語言開發(fā) JNI ,甚至于 JDK 也提供了一個 javah 工具來自動生成 C 語言程序框架。但是,對于廣大的 Delphi 程序員來說,難道就不能用自己喜愛的 Delphi Java 互通消息了嗎?

    通過對 javah 生成的 C 程序框架和 JDK 中的 jni.h 文件的分析,我們發(fā)現(xiàn), Java 利用 JNI 訪問本地代碼的關(guān)鍵在于 jni.h 中定義的 JNINativeInterface_ 這個結(jié)構(gòu) (Struct) ,如果用 Delhpi 語言改寫它的定義,應(yīng)該也可以開發(fā) JNI 的本地代碼。幸運的是,在網(wǎng)上有現(xiàn)成的代碼可以幫助你完成這個繁雜的工作,在 http://delphi-jedi.org 上提供了一個 jni.pas 文件,就是用 Delphi 語言重寫的 jni.h 。我們只需在自己的 Delphi 工程中加入 jni.pas 就可以方便地開發(fā)出基于 Delphi 語言的 JNI 本地代碼。

    本文將利用 jni.pas ,討論用 Delphi 語言開發(fā) JNI 本地代碼的基本方法。

    先來看一個經(jīng)典的 HelloWorld 例子。編寫以下 Java 代碼:

    class HelloWorld

    {

    ? public native void displayHelloWorld();

    ? static

    ? {

    ??? System.loadLibrary("HelloWorldImpl");

    ? }

    }

    這段代碼聲明了一個本地方法 displayHelloWorld ,它沒有參數(shù),也沒有返回值,但是希望它能在屏幕上打印出“您好!中國。”字樣。這個任務(wù)我們打算交給了本地的 Delphi 來實現(xiàn)。同時,在這個類的靜態(tài)域中,用 System.loadLibrary() 方法裝載 HelloWorldImpl.dll 注意,這里只需要給出文件名而不需要給出擴(kuò)展名 dll

    這時候,如果在我們的 Java 程序中使用 HelloWorld 類的 displayHelloWorld 方法,系統(tǒng)將拋出一個 java.lang.UnsatisfiedLinkError 的錯誤,因為我們還沒有為它實現(xiàn)本地代碼。

    下面再看一下在 Delphi 中的本地代碼的實現(xiàn)。新建一個 DLL 工程,工程名為 HelloWorldImpl ,輸入以下代碼:

    Uses

    ? JNI;

    procedure Java_HelloWorld_displayHelloWorld(PEnv: PJNIEnv; Obj: JObject);stdcall;

    begin

    ? Writeln(' 您好!中國。 ');

    end;

    exports

    ? Java_HelloWorld_DisplayHelloWorld;

    end.

    這段代碼首先導(dǎo)入 jni.pas 單元。然后實現(xiàn)了一個叫 Java_HelloWorld_displayHelloWorld 的過程,這個過程的命名很有講究,它以 Java 開頭,用下劃線將 Java 類的包名、類名和方法名連起來。這個命名方法不能有誤,否則, Java 類將無法將 nativ 方法與它對應(yīng)起來。同時,在 Win32 平臺上,此過程的調(diào)用方式只能聲明為 stdcall 雖然在 HelloWorld 類中聲明的本地方法沒有參數(shù),但在 Delphi 中實現(xiàn)的具體過程則帶有兩個參數(shù): PEnv : PJNIEnv Obj : JObject 。(這兩種類型都是在 jni.pas 中定義的)。其中, PEnv 參數(shù)代表了 Jvm 環(huán)境,而 Obj 參數(shù)則代表調(diào)用此過程的 Java 對象。當(dāng)然,這兩個參數(shù),在我們這個簡單的例子中是不會用到的。因為我們編譯的是 dll 文件,所以在 exports 需要輸出這個方法。

    編譯 Delphi 工程,生成 HelloWorldImp.dll 文件,放在運行時系統(tǒng)能夠找到的目錄,一般是當(dāng)前目錄下, 并編寫調(diào)用 HelloWorld 類的 Java 類如下:

    class MainTest

    {

    ? public static void main(String[] args)

    ? {

    ??? new HelloWorld().displayHelloWorld();

    ? }

    }

    運行它,如果控制臺輸出了“您好!中國。”,恭喜你,你已經(jīng)成功地用 Delphi 開發(fā)出第一個 JNI 應(yīng)用了。

    接下來,我們稍稍提高一點,來研究一下參數(shù)的傳遞。還是 HelloWorld ,修改剛才寫的 displayHelloWorld 方法,讓顯示的字符串由 Java 類動態(tài)確定。新的 displayHelloWorld 方法的 Java 代碼如下:

    public native void displayHelloWorld(String str);

    修改 Delphi 的代碼,這回用到了過程的第一個固有參數(shù) PEnv ,如下:

    procedure Java_HelloWorld_displayHelloWorld(PEnv: PJNIEnv; Obj: JObject; str: JString); stdcall;

    var

    ? JVM: TJNIEnv;

    begin

    ? JVM := TJNIEnv.Create(PEnv);

    ? Writeln(JVM.UnicodeJStringToString(str));

    ? JVM.Free;

    end;

    在該過程的參數(shù)表中我們增加了一個參數(shù) str : JString ,這個 str 就負(fù)責(zé)接收來自 HelloWorld 傳入的 str 實參。注意實現(xiàn)代碼的不同,因為使用了參數(shù),就涉及到參數(shù)的數(shù)據(jù)類型之間的轉(zhuǎn)換。從 Java 程序傳過來的 Java String 對象現(xiàn)在成了特殊的 JString 類型,而 JString Delphi 中是不可以直接使用的。需要借助 TJNIEnv 提供的 UnicodeJStringToString() 方法來轉(zhuǎn)換成 Delphi 能識別的 string 類型。所以,需要構(gòu)造出 TJNIEnv 的實例對象,使用它的方法( TJNIEnv 提供了眾多的方法,這里只使用了它最基本最常用的一個方法),最后,記得要釋放它。對于基本數(shù)據(jù)類型的參數(shù),從 Java 傳到 Delphi 中并在 Delphi 中使用的步驟就是這么簡單。

    我們再提高一點點難度,構(gòu)建一個自定義類 Book ,并把它的實例對象作為參數(shù)傳入 Delphi ,研究一下在本地代碼中如何訪問對象參數(shù)的公共字段。

    首先,定義一個簡單的 Java Book ,為了把問題弄得稍微復(fù)雜一點,我們在 Book 中增加了一個 java.util.Date 類型的字段,代碼如下:

    public class Book

    {

    ? public String title;? // 標(biāo)題

    ? public double price; // 價格

    ? public Date pdate;? // 購買日期

    }

    同樣,在 HelloWorld 類中增加一個本地方法 displayBookInfo ,代碼如下:

    public native void displayBookInfo(Book b);

    Delphi 的代碼相對于上面幾個例子來說,顯得復(fù)雜了一點,先看一下代碼:

    procedure Java_HelloWorld_displayBookInfo(PEnv: PJNIEnv; Obj: JObject; b:JObject); stdcall;

    var

    ?JVM: TJNIEnv;

    ?c,c2: JClass;

    ?fid:JFieldID;

    mid:JMethodID;

    title,datestr:string;

    price:double;

    pdate:JObject;

    begin

    ? JVM := TJNIEnv.Create(PEnv);

    ? c:=JVM.GetObjectClass(b);

    ? fid:=JVM.GetFieldID(c,'title','Ljava/lang/String;');

    ? title:=JVM.UnicodeJStringToString(JVM.GetObjectField(b,fid));

    ? fid:=JVM.GetFieldID(c,'price','D');

    ? price:=JVM.GetDoubleField(b,fid);

    ? fid:=JVM.GetFieldID(c,'pdate','Ljava/util/Date;');

    ? pdate:=JVM.GetObjectField(b,fid);

    ? c2:=JVM.GetObjectClass(pdate);

    ? mid:=JVM.GetMethodID(c2,'toString','()Ljava/lang/String;');

    ? datestr:=JVM.JStringToString(JVM.CallObjectMethodA(pdate,mid,nil));

    ?

    ? WriteLn(Format('%s? %f ?%s',[title,price,datestr]));

    ?

    ? JVM.Free;

    end;

    參數(shù) b:JObject 就是傳入的 Book 對象。先調(diào)用 GetObjectClass 方法,根據(jù) b 對象獲得它所屬的類 c ,然后調(diào)用 GetFieldID 方法從 ? 中獲取一個叫做 title 的屬性的字段 ID 一定要傳入正確的類型簽名。然后通過 GetObjectField 方法就可以根據(jù)得到的字段 ID 從對象中得到字段的值。注意這里的次序:我們得到傳入的對象參數(shù) (Object) ,就要先得到它的類 (Class) ,這樣既有了對象實例,又有了類,以后就從類中得到字段 ID ,根據(jù)字段 ID 從對象中得到字段值。對于類的靜態(tài)字段,則可以直接從類中獲取它的值而不需要通過對象。 如果要調(diào)用對象的方法,操作步驟也基本類似,也需要從類中獲取方法 ID ,再執(zhí)行對象的相應(yīng)方法。在本例中,因為我們增加了一個 java.util.Date 類型的字段,要訪問這樣的字段,也只能先把它做為 JObject 讀入,再以同樣的方法進(jìn)一步去訪問它的成員(屬性或方法)。本例中演示了如何訪問 Date 對象的成員方法 toString

    要正確地訪問類對象的成員屬性(字段)及成員方法,最重要的一點是一定要給出正確的簽名,在 Java 中對于數(shù)據(jù)類型和方法的簽名有如下的約定:

    數(shù)據(jù)類型 / 方法

    簽名

    byte

    B

    char

    C

    double

    D

    float

    F

    int

    I

    long

    J ( 注意:是 J 不是 L)

    short

    S

    void

    V

    boolean

    Z (注意:是 Z 不是 B

    類類型

    L 跟完整類名,如 Ljava/lang/String; (注意:以 L 開頭,要包括包名,以斜杠分隔,最后有一個分號作為類型表達(dá)式的結(jié)束)

    數(shù)組 type[]

    [type ,例如 float[] 的簽名就是 [float ,如果是二維數(shù)組,如 float[][] ,則簽名為 [[float ,(注意:這里是兩個 [ 符號)。

    方法

    ( 參數(shù)類型簽名 ) 返回值類型簽名,例如方法: float fun(int a,int b) ,它的簽名為 (II)F ( 注意:兩個 I 之間沒有逗號! ) ,而對于方法 String toString() ,則是 ()Ljava/lang/String;

    通過上面的例子,我們了解了訪問對象參數(shù)的成員屬性或方法的基本步驟和多個 Get 方法的使用。 TJNIEnv 同時提供了多個 Set 方法,可以修改傳入的對象參數(shù)的字段值,因為 Java 對象參數(shù)都是以傳址的方式進(jìn)行傳遞的,所以修改的結(jié)果可以在 Java 程序中得到反映。 TJNIEnv 提供的 Get/Set 方法,都需要兩個基本參數(shù):對象實例( JObject 類型)和字段 ID JField 類型),就可以根據(jù)提供的對象和字段 ID 來獲取或設(shè)置這個對象的這個字段的值。

    現(xiàn)在我們了解了在 Delphi 代碼中使用以及修改 Java 對象的操作步驟。進(jìn)一步,如果需要在 Delphi 中從無到有地創(chuàng)建一個新的 Java 對象,可以嗎?再來看一個例子,在 Delphi 中創(chuàng)建 Java 類的實例,操作方法其實也非常簡單。

    先在 Java 代碼中增加一個本地方法,如下:

    ?public native Book findBook(String t);

    然后,修改 Delphi 代碼,增加一個函數(shù)(因為有返回值,所以不再是過程而是函數(shù)了):

    function Java_HelloWorld_findBook(PEnv: PJNIEnv; Obj: JObject; t:JString):JObject; stdcall;

    var

    ?JVM: TJNIEnv;

    ?c: JClass;

    ?fid:JFieldID;

    ?b:JObject;

    ?mid:JMethodID;

    begin

    ? JVM := TJNIEnv.Create(PEnv);

    ?

    ? c:=JVM.FindClass('Book');

    ? mid:=JVM.GetMethodID(c,'<init>','()V');

    ? b:=JVM.NewObjectV(c,mid,nil);

    ? fid:=JVM.GetFieldID(c,'title','Ljava/lang/String;');

    ? JVM.SetObjectField(b,fid,t);

    ? fid:=JVM.GetFieldID(c,'price','D');

    ? JVM.SetDoubleField(b,fid,99.8);

    ? Result:=b;

    ?

    ? JVM.Free;

    end;

    這里先用 FindClass 方法根據(jù)類名查找到類,然后獲取構(gòu)造函數(shù)的方法 ID ,構(gòu)造函數(shù)名稱固定為“ <init> ”,注意簽名為“ ()V ”說明使用了 Book 類的一個空的構(gòu)造函數(shù)。然后就是使用方法 NewObjectV 根據(jù)類和構(gòu)造函數(shù)的方法 ID 來創(chuàng)建類的實例。創(chuàng)建了類實例,再對它進(jìn)行操作就與前面的例子沒有什么兩樣了。對于非空的構(gòu)造函數(shù),則略為復(fù)雜一點。需要設(shè)置它的參數(shù)表。還是上面的例子,在 Book 類中增加一個非空構(gòu)造函數(shù):

    public Book(Strint t,double p){

    ?this.title=t;

    this.price=p;

    }

    Delphi 代碼中, findBook 函數(shù)修改獲取方法 ID 的代碼如下:

    mid:=JVM.GetMethodID(c,'<init>','(Ljava/lang/String;D)V');

    構(gòu)造函數(shù)名稱仍是“ <init> ”,方法簽名表示它有兩個參數(shù),分別是 String double 。然后就是參數(shù)的傳入了,在 Delphi 調(diào)用 Java 對象的方法如果需要傳入?yún)?shù),都需要構(gòu)造出一個參數(shù)數(shù)組。在變量聲明中加上:

    args : array[0..1] of JValue;

    注意!參數(shù)都是 JValue 類型,不管它是基本數(shù)據(jù)類型還是對象,都作為 JValue 的數(shù)組來處理。在代碼實現(xiàn)中為參數(shù)設(shè)置值,并將數(shù)組的地址作為參數(shù)傳給 NewObjectA 方法:

    ? args[0].l:=t; // t 是傳入的 JString 參數(shù)

    ? args[1].d:=9.8;

    ?

    ? b:=JVM.NewObjectA(c,mid,@args);

    JValue 類型的數(shù)據(jù)設(shè)置值的語句有點特殊,是吧?我們打開 jni.pas ,查看一下 JValue 的定義,原來它是一個 packed record ,已經(jīng)包括了多種數(shù)據(jù)類型, JValue 的定義如下:

    ? JValue = packed record

    ? case Integer of

    ??? 0: (z: JBoolean);

    ??? 1: (b: JByte?? );

    ??? 2: (c: JChar?? );

    ??? 3: (s: JShort? );

    ??? 4: (i: JInt??? );

    ??? 5: (j: JLong?? );

    ??? 6: (f: JFloat? );

    ??? 7: (d: JDouble );

    ??? 8: (l: JObject );

    ? end;

    下面再來看一下錯誤處理,在調(diào)試前面的例子中,大家也許看到了一旦在 Delphi 的執(zhí)行過程中發(fā)生了錯誤,控制臺就會輸出一大堆錯誤信息,如果想要屏蔽這些信息,也就是說希望在 Delphi 中捕獲錯誤并直接處理它,應(yīng)該怎么做?也很簡單,在 TJNIEnv 中提供了兩個方法可以方便地處理在訪問 Java 對象時發(fā)生的錯誤。

    var

    … …

    ae:JThrowable;

    begin

    … …

    ae:=JVM.ExceptionOccurred;

    ? if ( ae<>nil ) then

    ?? begin

    ??? Writeln(Format('Exception handled in Main.cpp: %d', [longword(ae)]));

    ??? JVM.ExceptionDescribe;

    ??? JVM.ExceptionClear;

    ?? end;

    … …

    用方法 ExceptionOccurred 可以捕獲 Java 拋出的錯誤,并存入 JThrowable 類型的變量中。用 ExceptionDescribe 可以顯示出 Java 的錯誤信息,而 ExceptionClear 顯然就是清除錯誤,讓它不再被拋出。

    至此,我們已經(jīng)把從 Java 代碼通過 JNI 技術(shù)訪問 Delphi 本地代碼的步驟做了初步的探討。在 jni.pas 中也提供了從 Delphi 中打開 Java 虛擬機(jī)執(zhí)行 Java 代碼的方法,有興趣的讀者不妨自己研究一下。

    posted on 2006-12-19 05:41 壞男孩 閱讀(1295) 評論(1)  編輯  收藏 所屬分類: java命令學(xué)習(xí)

    FeedBack:
    # re: 用Delphi開發(fā)JNI(Java Native Interface)應(yīng)用
    2006-12-22 14:46 | ghost
    東西真多,慢慢看  回復(fù)  更多評論
      
    主站蜘蛛池模板: 亚洲国产精品久久久久| 香港经典a毛片免费观看看| 成在人线av无码免费高潮水| 成人毛片视频免费网站观看| 国产l精品国产亚洲区在线观看| 亚洲av无码专区在线电影天堂| 日韩免费无码一区二区三区 | 久久精品国产亚洲AV大全| 免费国产a理论片| 欧美三级在线电影免费| 亚洲免费视频网站| eeuss影院免费直达入口| 成人爱做日本视频免费| 亚洲youjizz| 69视频免费在线观看| 亚洲日韩一页精品发布| 免费国产在线精品一区| 狠狠久久永久免费观看| 亚洲人成伊人成综合网久久| 午夜理伦剧场免费| 亚洲精品你懂的在线观看| 一区二区免费在线观看| 国产jizzjizz视频全部免费| 亚洲码和欧洲码一码二码三码| 美女视频黄是免费的网址| 亚洲精品美女久久久久9999| 野花香在线视频免费观看大全| 国产偷窥女洗浴在线观看亚洲| 视频一区在线免费观看| 国产亚洲福利一区二区免费看| 亚洲精品无码mⅴ在线观看| 97视频热人人精品免费| 亚洲一区在线视频观看| 57pao一国产成视频永久免费| 亚洲av无码一区二区三区网站| 伊人免费在线观看高清版| 国产亚洲大尺度无码无码专线| 一区二区三区免费视频网站| 国产在线19禁免费观看| 久久精品国产亚洲AV| 国产免费无遮挡精品视频|