通过 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('雅林网络 http://zmzx.icpcn.com')));
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 了解更多