通過 JNI 讓 JAVA 與 Delphi 程序交互(五)
之前,我們學了如何用 Java 調用 Delphi 程序的一個方法
如果在Delphi 程序在適當時候需要調用 Java 程序,又要怎么做呢?
首先,我們先定義如下的 Java 類:
//------------------------------------------------------------------------------
package alvinJNI;
class HelloWorld {
??????? static {
??????????????? System.loadLibrary("DelphiAction");
??????? }
??????? String str = "你好";
???????
??????? public native void callPrintText(HelloWorld hw);
?
??????? public void printText(String arg) {
??????????????? System.out.println(arg);
??????? }
???????
??????? public static void main(String[] args) {
??????????????? HelloWorld hw = new HelloWorld();
??????????????? hw.callPrintText(hw);
??????? }
}
//-------------------------------------------------------------------------------
我們再像上次一樣在 Delphi 中建立 DLL 工程,寫下面的代碼(有注釋):
//-------------------------------------------------------------------------------
library DelphiAction;
uses
? JNI;
//今天的這個程序稍微的復雜一點,因為要調用 Java 對象的方法,在這里可以學到對 JObject 的操作
procedure Java_alvinJNI_HelloWorld_callPrintText(PEnv: PJNIEnv; Obj: JObject; arg: JObject); stdcall;
var
? JVM: TJNIEnv;
? c: JClass;? //類ID
? fid: JFieldID;? //屬性ID
? mid: JMethodID;? //方法ID
? tmpStr: JString;
? javaargs : array[0..0] of JValue; //調用方法時的參數
begin
? JVM := TJNIEnv.Create(PEnv);
? {我們先來看下如何獲得一個對象的某個屬性值}
? {----------------------------------------}
? {我們對 Java 對象的操作要選獲取這個對象的 ClassID,我們可以用下面的方法來取得.}
? c := JVM.GetObjectClass(arg);
? {我們先來獲取參數 HelloWorld arg 對象的 String str 這個屬性的值
?? 這里我們先要獲得這個屬性在它所在類中的屬性 ID }
? fid := JVM.GetFieldID(c, 'str', 'Ljava/lang/String;');
? {上面調用的這個方法中的參數分別是: 所屬類ID, 屬性名, 屬性類型簽名
?? 關于屬性類型的簽名,將在下面 '說明1' 給出}
? {下面,我們就可以根據 屬性ID 來獲取屬性值了, 這里我們會取得到 arg.str 這個字符串}
? tmpStr := JVM.GetObjectField(arg, fid);
? {上面的這個 JVM.GetObjectField(arg, fid) 用來獲取屬性值
?? 參數分別是: 要取得其屬性的對象, 要取得的屬性的屬性ID
?? 這里取得的是一個 Java 的 String 對象,是 JString,其實它也就是 JObject 類型的}
? writeln('Delphi 輸出的: ' + JVM.UnicodeJStringToString(tmpStr));
?
? {我們再來看下如何調用一個 JObject 的方法, 這里我們要調用的是 arg.printText() 這個方法}
? {------------------------------------------------------------------------------------}
? //我們還是要用到上面的那個 類ID: c.
? //這一次我們要取得這個方法的 方法ID
? mid := JVM.GetMethodID(c, 'printText', '(Ljava/lang/String;)V');
? //上面調用的這個方法中的參數分別是: 所屬類ID, 方法名, 方法(參數+返回值)類型簽名
? //關于方法(參數+返回值)類型的簽名,將在下面 '說明2' 給出
? //有了 方法ID 后我們就可以用這個ID來調用這個方法了,我們這里要調用的方法是: arg.printText(參數);
? //因為我們要調用的這個方法有參數, 調用 Java 方法的時候如果有參數,要建立參數數組,這里我們就來建立數組
? javaargs[0].l := tmpStr;
? {這里這個 javaargs 是 JValue 類型. 它有點特殊,它的用法在下面 說明3 給出}
? {有了 類象, 方法ID, 參數. 下面我們就可以調用 arg.printText(javaargs) 這個方法了,使用下面這個方法就可實現}
? JVM.CallObjectMethodA(arg, mid, @javaargs);
? JVM.Free;
end;
exports
??????? Java_alvinJNI_HelloWorld_callPrintText;
end.
//--------------------------------------------------------------------------------
到這里,我們已經可以從 Delphi 中獲得 Java 對象的屬性了, 還可以調用一個 Java 對象的方法,是不是很酷呢?
你學到了沒?
###########################說明1###############################
現在,我們還要再了解一個獲取 "屬性ID" 時的那個簽名
上面例子中: fid := JVM.GetFieldID(c, 'str', 'Ljava/lang/String;'); 用的簽名是: 'Ljava/lang/String;'
因為剛剛要獲得的屬性是 java.lang.String 類型的,所以它的簽名是: 'Ljava/lang/String;'
如果,我們要獲得的屬性是其它類型,獲取 屬性ID 時又要怎樣簽名呢?下面給出一個對照表
byte -- B
char --- C
double -- D
float -- F
int -- I
long -- J (注意:是J不是L)
short -- S
void -- V
boolean - Z(注意:是Z不是B)
class(類對象類型) - 'L'+完整類名+';'? (包路徑分隔符為: '/'.?? 如上面例子中的 String 對型, 簽名為: 'Ljava/lang/String;')
數組 type[] -- '['+type (例如 float[] 的簽名就是 '[float')
(如果是二維數組,如float[][],則簽名為 '[[float')
############################說明2###############################
現在,我們還要再了解一個獲取 "方法ID" 時的那個簽名
上面例子中: mid := JVM.GetMethodID(c, 'printText', '(Ljava/lang/String;)V'); 用的簽名是: '(Ljava/lang/String;)V'
方法ID 的簽名,分為兩部分
一部分是前面括號中的,是參數類型的簽名
另一部分是括號后的,是返回值類型的簽名
其中某個簽數與返回值的類型簽名與獲取屬性ID時的簽名是一樣的
上面要調用的方法只有一個參數,如果有多個參數時又怎樣呢?
如: int getInt(long a, double b); 這樣的 Java 方法要這樣簽名: '(JD)I'
(注意:參數簽名是連續的,沒有分隔符, 這里第一個參數 long 簽名為:J, 第二個參數簽名為: D, 返回值類型 int 簽名為: I)
說到這里,相信大家都會使用這個簽名了
############################說明3###############################
在調用一個 Java 方法時, 如果這個方法有參數, 我們就要傳遞一個參數數組的地址給 Java
現在,我們還要再了解如何創建這樣的一個參數數組
傳遞給 Java 方法的參數,類型均為 JValue. 它是一個packed record
如果,我們要調用的方法 void myMethod(int a, long b, String c); 有 3 個參數
那么
1.我們先要聲明如下數組:
var
? args : array[0..1] of JValue;
2.給數組賦值
? args[0].i := 100;
? args[1].j := 100;
? args[2].l := JVM.StringToJString(pchar(UTF8Encode('雅林網絡 )));
3.調用
? JVM.CallVoidMethodA(Java對象, 方法ID, @args);
JValue 是一個 packed record,它的定義如下:
? 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;
調用方法時,TJNIEnv 還有:
??? CallObjectMethodA: function(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue): JObject; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallBooleanMethodA: function(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue): JBoolean; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallByteMethodA: function(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue): JByte; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallCharMethodA: function(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue): JChar; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallShortMethodA: function(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue): JShort; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallIntMethodA: function(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue): JInt; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallLongMethodA: function(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue): JLong; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallFloatMethodA: function(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue): JFloat; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallDoubleMethodA: function(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue): JDouble; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallVoidMethodA: procedure(Env: PJNIEnv; Obj: JObject; MethodID: JMethodID; Args: PJValue); {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualObjectMethodA: function(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue): JObject; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualBooleanMethodA: function(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue): JBoolean; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualByteMethodA: function(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue): JByte; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualCharMethodA: function(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue): JChar; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualShortMethodA: function(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue): JShort; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualIntMethodA: function(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue): JInt; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualLongMethodA: function(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue): JLong; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualFloatMethodA: function(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue): JFloat; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualDoubleMethodA: function(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue): JDouble; {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
??? CallNonvirtualVoidMethodA: procedure(Env: PJNIEnv; Obj: JObject; AClass: JClass; MethodID: JMethodID; Args: PJValue); {$IFDEF MSWINDOWS} stdcall; {$ENDIF} {$IFDEF LINUX} cdecl; {$ENDIF}
好了,到這里,相信大家對做 Delphi 的 JNI 已有一定的了解
關于 Delphi JNI 的話題就先說到這里
如果有興趣,大家可以打開 jni.pas 了解更多