聚合 管理  

Blog Stats

News

我使用新博客啦:
http://www.linjunhai.com/
大家到我的新博客上看看吧!

随笔分类(28)

文章分类(4)

随笔档案(53)

文章档案(4)

相册

相关链接


林俊海的博客

超级大菜鸟,每天要自强!

通过 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 了解更多

posted on 2006-12-13 02:20 林俊海 阅读(1715) 评论(4)  编辑  收藏 所属分类: JAVA天地

评论

# re: 通过 JNI 让 JAVA 与 Delphi 程序交互(五) 2007-05-24 17:14 sunboylyg
大侠,你这个在Delphi里调用java方法的例子好像没法用:
你的例子里面先是在java里调用Delphi方法,同时把自己当作参数(arg)传给Delphi,然后才在Delphi里利用这个参数调用java方法。
说白了,还是java调用delphi的例子,如果我想直接在delphi里调用java,那么上面说道的arg从哪里得来呢?困惑中……  回复  更多评论
  

# re: 通过 JNI 让 JAVA 与 Delphi 程序交互(五) 2007-05-24 17:22 sunboylyg
高人啊!看了你这一系列的文章,真是收益匪浅,还望指点迷津,到底怎么样才能在delphi里调用java呢,上面的疑问怎么解决?  回复  更多评论
  

# re: 通过 JNI 让 JAVA 与 Delphi 程序交互(五) 2007-05-25 08:14 林志斌
http://www.blogjava.net/Files/alvin/DelphiCallJava.rar
上面是 Delphi 主动调用 Java 的一个例子,sunboylyg 可以看一下  回复  更多评论
  

# re: 通过 JNI 让 JAVA 与 Delphi 程序交互(五) 2007-05-25 08:37 sunboylyg
大侠行侠仗义,慷慨大方,实在令人敬佩!只可惜,鄙人才疏学浅,未有以报也……  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: