Java 技巧 101:applet 间通信的替代方法

Java 技巧 101:applet 间通信的替代方法

作者 Tobias Hill



摘要
您可能认为能让 applet 彼此通信的唯一选择就是使用 getApplet。不幸的是,getApplet 方法仅返回与发出调用的 applet 在同一个 HTML 页面上的 applet,这样就限制了您通过 applet 间的通信构建有趣界面的方式。这篇技巧说明的替代方法能使处于不同框架甚至不同浏览器窗口中的 applet 彼此调用对方的方法。

java.applet 包中的 AppletContext 类包含两个成员方法,即 getApplet 和 getApplets。通过使用这两个方法,一个 applet 就可以找到其他 applet 并调用它们的方法。要这样做必须满足下面的安全要求:

  • 这些 applet 来自同一个服务器上的同一个目录中。
  • 这些 applet 运行于同一个浏览器窗口中的同一个页面上。

    这样设计安全限制可能有很好的原因;但是,后一个要求限制了利用 applet 间的通信制作有趣的多 applet 界面的方式。

    试考虑这样一种情况:

    您刚编好一个很好的股票市场交易 applet,并决定为它编写一个良好的帮助系统。您希望帮助系统也是一个 applet,并希望将它与股票市场交易 applet 在不同的浏览器框架中运行。您作出这个决定可能是出于网站结构方面的考虑,也可能是出于始终显示帮助系统的需要。另外,您希望使帮助系统根据用户当前在股票交易 applet 中进行的操作转至正确的信息/指导(就像 Microsoft Office 套件中的“回形针”一样)。您甚至计划在帮助系统中编制向导,这些向导可远程指出问题,并可远程执行股票市场交易 applet 中的任务。

    这一方案中体现的思想很不错。但是,因为这两个 applet 处于不同的页面上,所以 AppletContext 中的 Java API 无法帮助您实现这个想法 -- 但这篇技巧可以帮助您。


    使用 AppletContext API

    在说明 applet 间通信的替代机制前,我将首先简要说明一下 getApplet 和 getApplets 这两个方法是如何工作的。一个 applet 通过使用 getApplet 方法可以按名称找到同一个 HMTL 页面中的另一个 applet,而通过使用 getApplets 方法可以找到同一个页面上的所有 applet。这两个方法如果成功执行,则会向调用者返回一个或多个 Applet 对象。调用者一旦找到一个 Applet 对象,它就可能调用这个 Applet 的公用方法。

    假定有下面这样一段 HTML 代码:
    PHP代码:
    <applet code="Applet1" width="400" height="100" name="app1">
    </
    applet>

    <
    applet code="Applet2" width="400" height="100" name="app2">
    </
    applet>

    通过使用 applet 标记中的 name 属性,您就可以用下面的方式引用一个特定的 applet:
    PHP代码:
    Applet theOtherApplet = getApplet("app1");
    theOtherApplet.anyMethod(); //调用任一个公用方法

    或者,您也可以用以下的代码来检索这个页面上的所有 applet:
    PHP代码:
    Enumeration allAppletsOnSamePage = getApplets();
    while(
    allAppletsOnSamePage.hasMoreElements()) {
    Applet appl = (Applet) allAppletsOnSamePage.nextElement();
    appl.anyMethod(); //调用任一个公用方法
    }

    当发出调用的 applet 在它所在的同一个 HTML 页面上检索到一个或几个 applet 之后,它就可以调用这些 applet 的公用方法。

    使用静态数据结构

    不幸的是,如果使用标准方法,则只能与同一个 HTML 页面中的 applet 通信。幸运的是,您很容易就可以避开这个限制。使 applet 间跨页面通信的方法基于这样一个事实,即如果两个 applet 的 codebase 相同,则即使它们是在不同的浏览器窗口中被加载的,它们也共享同一个运行时环境。粗略地说,codebase 就是从中加载 applet 的那个目录。请参阅文后的参考资源,其中有一个链接指向有关 codebase 的一篇教程。

    由于运行时环境是共享的,因此所有 applet 实例都可以访问静态域和静态结构,这样这些静态域和结构就可用来在不同 applet 之间传递信息。

    applet 不仅可以存储诸如整数、字符和字符串这样的简单数据类型,而且每个 applet 都可以将其自身(实例)的一个引用存储在一个静态域(可能在它自己的类中)中。任何 applet 都可以访问这个域,从而获得指向这个实例的引用。

    这听起来复杂吗?不,一点也不复杂。我首先举一个简单的例子。假定您的一个 applet (AppletA.class) 在一个框架中,而另一个 applet (AppletB.class) 在另一个框架中,而且这两个 applet 都是从同一个 codebase 加载的。

    您现在希望授予 AppletA 访问 AppletB 的公用方法的权限。您必须让 AppletB 将其自身的一个引用存储在一个静态公用域中,就像下面这样:
    PHP代码:
    public class AppletB {
    public static
    AppletB selfRef = null; // 初始归零

    public void init() {
    // 生成对该实例的引用
    selfRef = this;
    }
    ...
    }

    现在您就可以从 AppletA 访问 AppletB 的实例了:
    PHP代码:
    public class AppletA {
    AppletB theOtherApplet = null;

    public
    void callAppletB() {
    // 获取静态域,其中存储着指向 AppletB 的
    // 实例的指针。
    theOtherApplet = AppletB.selfRef;

    // 此后就可以调用实例方法了,
    // 如下所示...
    theOtherApplet.repaint();
    }
    ...
    }

    这就是我们所要做的全部工作。因为运行时环境是由不同的 applet 共享的,所以即便 applet 不在同一个页面上,这个方法同样奏效。

    值得注意的一点是,上面的代码并没有处理在启动 AppletB 之前就调用 AppletA 中的 callAppletB 方法的情况。如果发生这种情况,则 selfRef 将是 null,这样不能进行任何通信。

    一种更通用的方法

    当然,还有一种更通用的方法。您可以创建这样一个类,创建它的唯一目的就是在静态数据结构中存储 applet 的引用。稍后您将看到的 AppletList 类就属于这种情况。希望其他 applet 访问自己的公用方法的 applet 实例通过 AppletList 将自己注册。按照 AppletContext.getApplet(string name) 中的模式,每个注册项都与一个字符串相关联。当一个 applet 调用某个 applet 的引用时,这个字符串就起关键字的作用。

    通常,applet 是按下面的方式注册的:
    PHP代码:
    public class AppletA {
    public
    void start() {
    AppletList.register("Stock-trade-applet", this);
    ...
    }
    }

    另一个 applet 获取对它的访问权:
    PHP代码:
    public class AppletB {
    public
    void run() {
    AppletA tradeApplet =
    (
    AppletA) AppletList.getApplet("Stock-trade-applet");
    ...
    }
    }

    当该 applet 停止运行时,您必须紧记在 AppletList 中撤销注册:
    PHP代码:
    public void stop() {
    AppletList.remove("Stock-trade-applet");
    ...
    }

    AppletList 类的完整源代码如下所示:
    PHP代码:
    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。

    局限性

    正如我在前面提到的那样,必须从同一个 codebase 中加载这些 applet。此外,如果浏览器的两个不同副本正在运行,并且 applet 被加载到每个副本中,则 applet 可能无法彼此通信(取决于浏览器的版本和设置),因为它们可能不再共享同一个运行时环境。但是,如果是浏览器本身衍生出新的浏览器窗口,则没有任何问题。

    该技巧已在几个平台和几个浏览器版本中通过测试,但在某些配置中每个 applet 的运行时环境可能是独立的。该技巧已在下面的操作系统和浏览器组合中通过测试:
  • Windows2000: Internet Explorer 5.0,Internet Explorer 5.5,Netscape Navigator 4.72,Opera 4.01
  • Windows 98: Internet Explorer 4.72,Internet Explorer 5.0,Netscape Navigator 4.02
  • Mac OS 9: Internet Explorer 4.5,
  • Netscape Navigator 4.5
    Red Hat 6.2: Netscape Navigator 4.73

    小结

    这篇技巧说明了能使 applet 彼此通信的一种替代方法。这种方法以 Java API 的 getApplet() 方法不支持的方式工作。这篇技巧中介绍的知识增大了将 applet 作为网站或内部网的一部分的可能性 -- 可以用它替代或补充 getApplets 方法。

    作者简介

    Tobias Hill 是 Citerus 的创办者之一,该公司以瑞典为基地,致力于在 Java 平台上构建因特网、内部网和外部网系统。Hill 从 1996 年开始用 Java 编程,参与了许多项目,从为自主控制的机器人编程到开发在线焰火明信片制作程序等等。

    参考资源

    * 说明如何使用 AppletList 类的示例:
    exampleCode.zip
    * Sun 提供的关于 applet 间通信的 Java 教程:
    http://web2.java.sun.com/docs/books...tsonly/iac.html
    * Sun 提供的有关 codebase(在其他问题中)的 Java 教程:
    http://web2.java.sun.com/docs/books...sonly/html.html
    * 查看以前的所有 Java 技巧以及提交您自己的技巧:
    http://www.javaworld.com/javatips/j...tips.index.html
  • 你可能感兴趣的:(Java 技巧 101:applet 间通信的替代方法)