[Java] PHPRPC 3.0.2 for Java/J2ME 中的异步调用

andot 2009-02-14
在大多数情况下,同步调用即可满足您的需要。但是有 2 种情况下用异步调用会更加方便一些:

首先是当做图像界面编程时,要防止单线程下同步调用出现卡死界面的现象,可以使用异步调用,但这种情况下,采用同步调用加多线程技术也可以解决。

另一种情况是,当使用同步调用加多线程时,如果你要获取服务器端输出重定向的内容或者要获取警告错误,则可能会获取到错误的内容,原因是另外的线程执行时也有可能改写 output 和 warning 属性的值。这种情况下,使用异步调用可以轻松解决。

因此,在 3.0.2 版本之后我们增加了对异步调用的支持。下面我们就看看如何实现异步调用。

如何实现异步调用?

异步调用跟同步调用类似,你可以通过接口方式,也可以通过直接 invoke 方式完成。不同的是,异步调用时,可以通过接口方式实现引用参数传递和为某个异步调用指定加密方式。而同步调用在接口方式下不支持这一点。

下面我们来看一下异步调用的例子(这个例子中也有同步调用):

import org.phprpc.*;

interface IHello {
    // 同步调用
    public String hello(String name);
    // 异步调用
    public void hello(String name, PHPRPC_Callback callback);
    // 可以设置引用参数传递的异步调用
    public void hello(String name, PHPRPC_Callback callback, boolean byRef);
    // 可以设置引用参数传递和指定加密方式的异步调用
    public void hello(String name, PHPRPC_Callback callback, boolean byRef, int encryptMode);

}
public class HelloWorld {
    public static void main (String [] args) {
        final PHPRPC_Client client = new PHPRPC_Client ("http://127.0.0.1:8080/index.aspx");
        final IHello clientProxy = (IHello) client.useService(IHello.class);
        client.setKeyLength(1024);
        client.setEncryptMode(2);

        // 回调方法的定义(handler1 到 handler4 都是回调方法)
        PHPRPC_Callback callback = new PHPRPC_Callback() {
            public void handler1(String result) {
                System.out.println("handler1:");
                System.out.println(result);
                System.out.println();
            }
            public void handler2(String result, Object[] args) {
                System.out.println("handler2:");
                System.out.println(result);
                System.out.println(args[0]);
                System.out.println();
            }
            public void handler3(String result, Object[] args, String output) {
                System.out.println("handler3:");
                System.out.println(result);
                System.out.println(args[0]);
                System.out.println(output);
                System.out.println();
            }
            public void handler4(String result, Object[] args, String output, PHPRPC_Error warning) {
                System.out.println("handler4:");
                System.out.println(result);
                System.out.println(args[0]);
                System.out.println(output);
                System.out.println(warning);
                System.out.println();
            }
            // 专门用于错误处理的回调方法,默认行为是什么都不做
            public void errorHandler(Throwable error) {
                System.out.println(error.toString());
            }
        };

        // 异步调用
        clientProxy.hello("World1", callback);
        // 功能同上
        clientProxy.hello("World2", callback, false);
        // 加密级别设为三级
        clientProxy.hello("World3", callback, false, 3);
        // 引用参数传递
        clientProxy.hello("World4", callback, true);
        // 引用参数传递,加密级别设为无
        clientProxy.hello("World5", callback, true, 0);

        // 采用直接 invoke 方式的异步调用
        client.invoke("hello", new Object[] {"World6"}, callback, false, 3);

        // 同步调用
        System.out.println(clientProxy.hello("World7"));
    }
}


上面这个例子中,IHello 接口定义了 1 个同步调用方法,3 个异步调用方法,当然这 4 个方法对于的服务器端方法都是同一个 hello。

后面在回调方法定义部分,我们采用了创建匿名类对象的方式,其中 handler1 到 handler4 这些方法名都是随意取的,它们叫什么名字都无所谓(比如叫 abc、efg 都是可以的),回调方法的个数也不是固定,回调方法可以一个也不定义,也可以定义许多个,没有个数限制,所以这些方法都会作为回调方法被执行。回调方法的参数个数是 1 到 4 个,参数顺序分别是 result、args、output、warning。

result 是服务器执行返回的结果,它的类型声明跟服务器端的类型应该相容,否则会产生转型错误,关于错误处理后面我们再介绍。

args 是方法的参数,当方法为异步调用时,参数值可能会有所改变。它的元素的实际类型与发起调用时也不一定相同,应该用 org.phprpc.util.Cast 的 cast 方法转换元素到实际类型。

output 是服务器端输出重定向的内容,它是字符串类型的。

warning 是服务器端发生的经过错误,它是 PHPRPC_Error 类型的。

在回调函数参数列表中,上述参数可以不用全部指定,但必须按上述顺序指定,中间不能跳过。

而 errorHandler 是一个特殊的回调方法,它只在发生错误时被执行,它的参数是 Throwable 类型的,你可以不用重新定义它,不过它的默认行为是什么都不做。

上面的程序在正确的情况下执行结果可能是这样的:

handler1:
hello World5

handler2:
hello World5
[B@70329f3d

handler3:
hello World5
[B@70329f3d
output: hello World5
handler1:
hello World6


handler4:
hello World5
handler2:
hello World6
World6

[B@70329f3d
output: hello World5
null

handler3:
hello World6
World6
output: hello World6

handler4:
hello World6
World6
output: hello World6
null

handler1:
hello World4

handler2:
hello World4
[B@b749757

handler3:
hello World4
[B@b749757
output: hello World4

handler4:
hello World4
[B@b749757
output: hello World4
null

handler1:
hello World3

handler2:
hello World3
World3

handler3:
hello World3
World3
output: hello World3

handler4:
hello World3
World3
output: hello World3
null

handler1:
hello World1

hello World7
handler2:
hello World1
World1

handler3:
hello World1
World1
output: hello World1

handler4:
hello World1
World1
output: hello World1
null

handler1:
hello World2

handler2:
hello World2
World2

handler3:
hello World2
World2
output: hello World2

handler4:
hello World2
World2
output: hello World2
null


因为是异步调用,所以你会发现输出的顺序并不是按照执行的顺序来的。另外,你会注意到 World4 和 World5 的调用在输出 args[0] 时,变成了一个字节数组地址,原因就是引用参数传递后,数据类型也是可以发生了变化的。

现在我们应该理解如何来进行异步调用了。

另外,在 J2ME 版本中,异步调用也不支持代理方式。另外,回调方法也只能定义一个,且名字和参数个数,参数类型都是固定的,具体格式参见 J2ME 版本的 PHPRPC_Callback 的定义。
Global site tag (gtag.js) - Google Analytics