Tricks of the Java Programming Gurus
by Glenn L. Vanderburg. et al.
1.Applet间的通讯
目录
考虑到你要完成的任务,一个applet,甚至几个独立的applet,有时可能都不够。还好,applet之间可以通讯,通过协作它们可以完成一些更复杂的任务。一组协作的applet所能产生的效能使单个applet所不能媲美的。
Applet之间的通讯可以通过传统方法实现:applet可以互相调用对方的成员方法或者通过socket或数据流通讯。事实上,applet间互相查找的途径有很多,每一种方法都有自身的优缺点。本文将讨论四种通讯机制,并给出一个较为复杂的例子,在这个例子中我们将使用其中一种通讯机制。
Java API 本身就有用来支持applet程序间协作的特性:AppletContext 类的 getApplet 和 getApplets 方法。设有这两个函数,applet程序可以通过名称查找并访问对方。你可以这样调用 getApplet:
Applet friend = getAppletContext().getApplet("Friend");
一旦调用结束,变量 friend 就成了名为"Friend"的applet的一个实例(instance) (如果这样的一个"Friend"applet存在的话)。例如:如果 "Friend" 是 Sun的 Animator applet 的一个实例,friend 将包含对这个实例的一个参考(reference)。
Applet的名字是在HTML中指定的,而不是在Java代码中。为了创建一个能被前面的实例代码所发现的animator applet,你可以在HTML插入如下几行:
<applet code="Animator.class" width=25 height=25 name="Friend"> <!-- applet parameters go here --> </applet>
getApplets 方法和 getApplet 差不多,只不过getApplets返回一个Enumeration,其中列举了所有可以进行访问的applet。然后我们就可以根据applet的不同属性来查询一个applet,包括applet的名字。下面示范了如何用getApplets查找一个名为"Friend" 的applet:
Applet friend; for( Enumeration e = getAppletContext().getApplets();e.hasMoreElements(); ){ try { Applet t = (Applet) e.nextElement(); if ("Friend".equals(t.getParameter("name"))) { friend = t; break; } } catch (ClassCastException e) { } }
显然,上述方法的工作很多,你不会用这种途径去查找一个applet。然而,如果你要查询多个applet,或是你不知道你所要查找的applet的确切的名字,这将是一种行之有效的方法。举个例子来说,用getApplets查找所有以"Helper"开头命名的applet将会很容易,你的applet就可以和同一页面中的所有的helper applet通讯了。
不幸的是,这种官方推荐的applet通讯机制至少有两大问题。首先,这种机制目前并没有被具体化,因此不同的应用程序对该机制有不同的实现。例如,getApplets 返回的是可以访问的applet,但是什么是可以访问?没有明确的定义。你可能得到的是同一页面上的applet,从同一个站点加载的applet,或者是两者的交集,这些都取决于运行applet的浏览器。这些问题因该促使Sun和Java's licensees制定出一个更加严密的,明确定义的applet协作机制。不过目前为止,这种applet通讯及值的不一致实现仍然是个令人头痛的问题。
另外一个问题就没有这么简单了。它看起来很容易理解,但它会使事情复杂到令很多applet程序员吃惊的地步。问题出在 getApplet 和 getApplets 不会让你得到一个还没有完全加载并初始化了的applet。由于网络的不确定和其他因素,例如applet的大小,我们不能决定页面上哪一个applet首先被加载,哪一个最后被加载。这就意味着我们原来的计划:首先启动并控制一个applet,查找其他applet,然后同它们协作,如果不增加额外的处理的话,是不能够达到预期目的的。
当然了,解决上述问题有很多方法。控制applet可以检查它的协作伙伴,如果它们还没有就绪,就进入等待睡眠(一秒钟左右),然后再次协作applet的状态,直到所有协作成员都已被初始化过。虽然这种方法效率很低,会导致较长的启动时间,怎么说它还是有效的。一个更好的解决方法是“双向查找、提醒机制”(two-way search-and-notification mechanism),当一个applet初始化完成后,控制这个applet查找其他applet并通知它们“我准备好了”。使用这种机制,所有辅助applet(helpers initialize)初始化完成后,主控applet将能够立即找到它们并开始协作,如果哪个辅助appplet迟一步完成初始化,主控applet也会被及时通知。
很多情况下,我们可以通过使用一个通用类的静态成员变量和方法构造一个内部applet通讯机制。如果多个applet都依赖于这个通用类,它们就可以把这个类作为一个消息的汇集点(rendezvous point),在那里注册它们的存在并察看其他applet的存在。
这里有个例子。如果在一个页面上ColorRelay applet被使用多次,不同的实例通过协作显示不同的图片(一些不同颜色的打字机色带)。你可以想象这些applet依靠一个标志位。如果一个applet显示了彩色的图片,其他的只能现实黑白的图片。图 1.1 是运行时的截图,清单 1.1 是 HTML 代码。
图 1.1 : ColorRelay applet 运行中
清单 1.1. ColorRelay.html.
<html> <body> <h1>Used Applets Sale!</h1>
<p> <applet align=baseline code="COM.MCP.Samsnet.tjg.ColorRelay.class" width=50 height=50 name="first"> <param name="flashColor" value="0x0000ff"> <param name="sleepTime" value="1"> <param name="image" value="spiral.gif"> </applet> Low, low prices!
<p> <applet align=baseline code="COM.MCP.Samsnet.tjg.ColorRelay.class" width=50 height=50> <param name="flashColor" value="0x00ff00"> </applet> This week only!
<p> <applet align=baseline code="COM.MCP.Samsnet.tjg.ColorRelay.class" width=50 height=50> <param name="flashColor" value="0xff0000"> <param name="sleepTime" value="3"> </applet> We won't be undersold!
</html>
清单 1.2 展示了 ColorRelay applet的程序框架,具体方法用注释代替了。我们将在后面的清单中提供完整的代码。
清单 1.2. ColorRelay.java (part 1).
/* * ColorRelay.java 1.0 96/04/14 Glenn Vanderburg */
package COM.MCP.Samsnet.tjg;
import java.applet.*; import java.awt.*; import java.awt.image.*;
/** * An applet which coordinates with other instances of itself on a Web * page to alternately flash copies of an image in different colors. * * @version 1.0, 14 Mar 1996 * @author Glenn Vanderburg */
public class ColorRelay extends Applet implements Runnable { // These are used to maintain the list of active instances static ColorRelay list, listTail; ColorRelay next, prev;
// This thread switches between instances static Thread relayer;
// This is the original, unmodified base image which all // of the instances use. static Image originalImage;
// The color that this instance uses to flash. White is the default. Color flashColor = Color.white;
// The modified, colorized image. Image modifiedImage;
// The image currently being displayed. This reference // alternates between originalImage and modifiedImage. Image image;
// We use a media tracker to help manage the images. MediaTracker tracker;
// The time we wait while flashing. Two seconds is the default. int sleepSecs = 2;
// Method: static synchronized // addToList(ColorRelay elem) Listing 1.3 // Method: static synchronized // removeFromList(ColorRelay elem) Listing 1.3 // Method: public init() Listing 1.4 // Method: public start() Listing 1.5 // Method: public stop() Listing 1.5 // Method: public run() Listing 1.5 // Method: public getAppletInfo() on CD // Method: public getParameterInfo () on CD // Method: public paint(Graphics g) on CD // Method: public update(Graphics g) on CD // Method: flash() on CD // Method: parseRGB(String str) on CD }
正如你看到的那样,程序中有一些普通的变量:两个Iamge,一个Color,一个MediaTracker,一个时间值,还有两个静态方法用来将 ColorRelay 对象插入链表。此外还有四个静态变量:originalImage, 当applet没有轮到发亮时显示;一个线程对象,用来协调applet之间的活动,还有applet链表的首位节点。
未完,待续。
翻译:chenyuan_tongji (chenyuan_tongji@sian.com)
|