java.applet包的appletcontext类包含如下两个成员方法:getapplet和getapplets。使用这两个方法,applet能够寻找到其他的applet并调用它们的方法。不过这是以满足如下安全条件为前提的:
所有applet位于同一服务器的同一目录。
所有applet运行在同一页面且位于同一浏览器窗口内。
或许为applet加上这些安全限制都有着很充分的理由,但第二个条件给我们实现带有applet到applet通信功能的多applet界面带来了限制。
请设想如下情形:
你刚好编写完了一个用于股票交易的applet,接下来想要为它做一个完善的帮助系统。这个帮助系统也是一个applet,而且应该放到和股票交易applet不同的另外一个帧里。你作出这个决定的原因可能是出于网站整体结构的考虑,也可能是你想让帮助系统总是处于显示状态。你还想让帮助applet能够自动根据用户的当前操作切换到相应的帮助条目(就象microsoft office帮助系统提供的那样)。此外,你甚至还计划着在帮助系统里做一个向导,使它能够在另外一个窗口中指导用户如何完成股票交易applet里的各种任务。
应当承认这种设想是非常美妙的。然而,由于这些applet位于不同的页面,appletcontext中的java api无法帮助你完全实现这种设想――不过本文介绍的技术能够。
在介绍新的applet到applet通信方法之前,我们先要简要地介绍一下getapplet和getapplets这两个方法的用法。applet能够寻找到同一页面的其他applet,这既可以调用getapplet方法通过名字查找,也可以调用getapplets方法找出同一页面内的所有applet。这两个方法都在调用成功时返回一个或多个applet对象给调用者。调用者找到目标applet对象之后,接着就可以调用它的公用方法。
假设html页面的代码片断如下:
通过applet标记的name属性,我们可以用如下代码引用指定的applet:
applet theotherapplet = getapplet("app1");
//调用该applet的公用方法
theotherapplet.anymethod();
或者,我们也可以用下面的代码提取页面内的所有applet:
enumeration allappletsonsamepage = getapplets();
while(allappletsonsamepage.hasmoreelements()) {
applet appl = (applet) allappletsonsamepage.nextelement();
// 调用applet的公用方法
appl.anymethod();
}
当发出调用的applet提取到同一html页面内的一个或多个applet对象时,它就可以调用这些applet对象的公用方法。
遗憾的是,使用这种标准方法我们只能实现同一页面内的applet通信。但如果幸运的话,我们可以很方便地突破这一局限。实现跨页面applet到applet通信基于这样一个事实,即如果两个applet具有相同的codebase,那么即使它们运行在不同的浏览器窗口中,它们也将共享同一个运行时环境。所谓的codebase,我们可以粗略地把它看成applet所在的服务器目录,请参见本文后面参考资源中有关codebase示范的链接。
共享运行时环境使得类的静态域和结构能够被所有的applet实例访问,因此,我们可以用这些静态域和结构在不同的applet之间传递信息。
我们不仅可以把简单数据类型――比如整数、字符、字符串存储到这些静态域,而且还可以存储applet实例本身的引用,这样,其他的applet就可以通过访问这些静态域得到该applet实例的引用。
这种方法非常复杂吗?事实上,它并不复杂。下面我们来看一个简单的例子。假设有两个applet(appleta.class和appletb.class)位于不同的帧,但它们具有相同的codebase。
现在我们要从appleta里面访问appletb的公用方法。首先我们要在appletb里面把它自己的引用保存到一个静态公用域,如:
public class appletb {
public static appletb selfref = null; // initially zero
public void init() {
// 保存当前实例的引用
selfref = this;
}
...
}
现在appleta可以访问appletb的实例:
public class appleta {
appletb theotherapplet = null;
public void callappletb() {
// 获得静态域的值,这个静态域保存了
// appletb实例的引用
theotherapplet = appletb.selfref;
// 接下来就可以调用appletb实例
// 的方法,例如:
theotherapplet.repaint();
}
...
}
这样就实现了两个applet的通信。由于不同的applet共享运行时环境,因此即使这些applet位于不同的页面,这种方法也同样有效。
不过应当注意的是,上面的代码不能处理这种情况:在appletb没有启动之前就在appleta里面调用callappletb方法。如果发生这种情况,则selfref的值将是null,applet之间的通信不能正常进行。
当然,我们还可以设计出更加通用的方法。我们可以创建一个类,这个类的唯一用途就是在自己的静态数据结构中保存其他applet的引用。下面是一个参考实现appletlist。想要让其他applet访问自己的公用方法的applet实例首先要在appletlist中注册。按照appletcontext.getapplet(string name)方法的处理模式,每个注册的applet都和一个字符串相关联。以后当其他applet需要引用某个applet实例时,这个字符串就可以作为键(即applet的标识)使用。
下面是applet在appletlist中注册的典型过程:
public class appleta {
public void start() {
appletlist.register("stock-trade-applet", this);
...
}
}
其他applet访问已注册applet的过程如下:
public class appletb {
public void run() {
appleta tradeapplet =
(appleta) appletlist.getapplet("stock-trade-applet");
...
}
}
当applet结束运行时它必须从applietlist取消注册:
public void stop() {
appletlist.remove("stock-trade-applet");
...
}
appletlist类的完整代码如下:
0: import java.util.*;
1: import java.applet.applet;
2:
3: public class appletlist {
4: private static hashtable applets = new hashtable();
5:
6: public static void register(string name, applet applet) {
7: applets.put(name,applet);
8: }
9:
10: public static void remove(string name) {
11: applets.remove(name);
12: }
13:
14: public static applet getapplet(string name) {
15: return (applet) applets.get(name);
16: }
17:
18: public static enumeration getapplets() {
19: return applets.elements();
20: }
21:
22: public static int size() {
23: return applets.size();
24: }
25: }
请从本文后面下载examplecode.zip了解更多有关如何应用appletlist类的信息。
本方法的局限
如前所述,参与通信的applet必须具有相同的codebase。此外,如果你运行的是两个不同的浏览器副本且applet分别运行于这两个浏览器中,由于这些applet可能没有共享运行时环境(这和浏览器版本、设置有关),因此它们可能不能进行通信。然而,如果你是从同一个浏览器创建出新的浏览器窗口,那么这个问题是不存在的。
本文所介绍的技术已经顺利通过好几个平台和浏览器版本的测试,但导致多个applet拥有各自的运行时环境的配置还是存在的。顺利通过测试的操作系统和浏览器组合如下所示:
小结
本文介绍了一种新的实现applet到applet通信的方法,这种方法在无法使用java api的getapplet()方法时仍旧有效。掌握了这种方法,你就有更多的机会在web网站或intranet上使用applet――用它来替换getapplets方法,或者补充getapplets方法的不足。
闽公网安备 35060202000074号