Java 技巧 101:applet 間通信的替代方法
作者 Tobias Hill摘要
您可能認(rèn)為能讓 applet 彼此通信的唯一選擇就是使用 getApplet。不幸的是,getApplet 方法僅返回與發(fā)出調(diào)用的 applet 在同一個 HTML 頁面上的 applet,這樣就限制了您通過 applet 間的通信構(gòu)建有趣界面的方式。這篇技巧說明的替代方法能使處于不同框架甚至不同瀏覽器窗口中的 applet 彼此調(diào)用對方的方法。
java.applet 包中的 AppletContext 類包含兩個成員方法,即 getApplet 和 getApplets。通過使用這兩個方法,一個 applet 就可以找到其他 applet 并調(diào)用它們的方法。要這樣做必須滿足下面的安全要求:
這樣設(shè)計(jì)安全限制可能有很好的原因;但是,后一個要求限制了利用 applet 間的通信制作有趣的多 applet 界面的方式。
試考慮這樣一種情況:
您剛編好一個很好的股票市場交易 applet,并決定為它編寫一個良好的幫助系統(tǒng)。您希望幫助系統(tǒng)也是一個 applet,并希望將它與股票市場交易 applet 在不同的瀏覽器框架中運(yùn)行。您作出這個決定可能是出于網(wǎng)站結(jié)構(gòu)方面的考慮,也可能是出于始終顯示幫助系統(tǒng)的需要。另外,您希望使幫助系統(tǒng)根據(jù)用戶當(dāng)前在股票交易 applet 中進(jìn)行的操作轉(zhuǎn)至正確的信息/指導(dǎo)(就像 Microsoft Office 套件中的“回形針”一樣)。您甚至計(jì)劃在幫助系統(tǒng)中編制向?qū)В@些向?qū)Э蛇h(yuǎn)程指出問題,并可遠(yuǎn)程執(zhí)行股票市場交易 applet 中的任務(wù)。
這一方案中體現(xiàn)的思想很不錯。但是,因?yàn)檫@兩個 applet 處于不同的頁面上,所以 AppletContext 中的 Java API 無法幫助您實(shí)現(xiàn)這個想法 -- 但這篇技巧可以幫助您。
使用 AppletContext API
在說明 applet 間通信的替代機(jī)制前,我將首先簡要說明一下 getApplet 和 getApplets 這兩個方法是如何工作的。一個 applet 通過使用 getApplet 方法可以按名稱找到同一個 HMTL 頁面中的另一個 applet,而通過使用 getApplets 方法可以找到同一個頁面上的所有 applet。這兩個方法如果成功執(zhí)行,則會向調(diào)用者返回一個或多個 Applet 對象。調(diào)用者一旦找到一個 Applet 對象,它就可能調(diào)用這個 Applet 的公用方法。
假定有下面這樣一段 HTML 代碼:
PHP代碼:
<applet code="Applet1" width="400" height="100" name="app1">
</applet>
<applet code="Applet2" width="400" height="100" name="app2">
</applet>
通過使用 applet 標(biāo)記中的 name 屬性,您就可以用下面的方式引用一個特定的 applet:
PHP代碼:
Applet theOtherApplet = getApplet("app1");
theOtherApplet.anyMethod(); //調(diào)用任一個公用方法
或者,您也可以用以下的代碼來檢索這個頁面上的所有 applet:
PHP代碼:
Enumeration allAppletsOnSamePage = getApplets();
while(allAppletsOnSamePage.hasMoreElements()) {
Applet appl = (Applet) allAppletsOnSamePage.nextElement();
appl.anyMethod(); //調(diào)用任一個公用方法
}
當(dāng)發(fā)出調(diào)用的 applet 在它所在的同一個 HTML 頁面上檢索到一個或幾個 applet 之后,它就可以調(diào)用這些 applet 的公用方法。
使用靜態(tài)數(shù)據(jù)結(jié)構(gòu)
不幸的是,如果使用標(biāo)準(zhǔn)方法,則只能與同一個 HTML 頁面中的 applet 通信。幸運(yùn)的是,您很容易就可以避開這個限制。使 applet 間跨頁面通信的方法基于這樣一個事實(shí),即如果兩個 applet 的 codebase 相同,則即使它們是在不同的瀏覽器窗口中被加載的,它們也共享同一個運(yùn)行時(shí)環(huán)境。粗略地說,codebase 就是從中加載 applet 的那個目錄。請參閱文后的參考資源,其中有一個鏈接指向有關(guān) codebase 的一篇教程。
由于運(yùn)行時(shí)環(huán)境是共享的,因此所有 applet 實(shí)例都可以訪問靜態(tài)域和靜態(tài)結(jié)構(gòu),這樣這些靜態(tài)域和結(jié)構(gòu)就可用來在不同 applet 之間傳遞信息。
applet 不僅可以存儲諸如整數(shù)、字符和字符串這樣的簡單數(shù)據(jù)類型,而且每個 applet 都可以將其自身(實(shí)例)的一個引用存儲在一個靜態(tài)域(可能在它自己的類中)中。任何 applet 都可以訪問這個域,從而獲得指向這個實(shí)例的引用。
這聽起來復(fù)雜嗎?不,一點(diǎn)也不復(fù)雜。我首先舉一個簡單的例子。假定您的一個 applet (AppletA.class) 在一個框架中,而另一個 applet (AppletB.class) 在另一個框架中,而且這兩個 applet 都是從同一個 codebase 加載的。
您現(xiàn)在希望授予 AppletA 訪問 AppletB 的公用方法的權(quán)限。您必須讓 AppletB 將其自身的一個引用存儲在一個靜態(tài)公用域中,就像下面這樣:
PHP代碼:
public class AppletB {
public static AppletB selfRef = null; // 初始?xì)w零
public void init() {
// 生成對該實(shí)例的引用
selfRef = this;
}
...
}
現(xiàn)在您就可以從 AppletA 訪問 AppletB 的實(shí)例了:
PHP代碼:
public class AppletA {
AppletB theOtherApplet = null;
public void callAppletB() {
// 獲取靜態(tài)域,其中存儲著指向 AppletB 的
// 實(shí)例的指針。
theOtherApplet = AppletB.selfRef;
// 此后就可以調(diào)用實(shí)例方法了,
// 如下所示...
theOtherApplet.repaint();
}
...
}
這就是我們所要做的全部工作。因?yàn)檫\(yùn)行時(shí)環(huán)境是由不同的 applet 共享的,所以即便 applet 不在同一個頁面上,這個方法同樣奏效。
值得注意的一點(diǎn)是,上面的代碼并沒有處理在啟動 AppletB 之前就調(diào)用 AppletA 中的 callAppletB 方法的情況。如果發(fā)生這種情況,則 selfRef 將是 null,這樣不能進(jìn)行任何通信。
一種更通用的方法
當(dāng)然,還有一種更通用的方法。您可以創(chuàng)建這樣一個類,創(chuàng)建它的唯一目的就是在靜態(tài)數(shù)據(jù)結(jié)構(gòu)中存儲 applet 的引用。稍后您將看到的 AppletList 類就屬于這種情況。希望其他 applet 訪問自己的公用方法的 applet 實(shí)例通過 AppletList 將自己注冊。按照 AppletContext.getApplet(string name) 中的模式,每個注冊項(xiàng)都與一個字符串相關(guān)聯(lián)。當(dāng)一個 applet 調(diào)用某個 applet 的引用時(shí),這個字符串就起關(guān)鍵字的作用。
通常,applet 是按下面的方式注冊的:
PHP代碼:
public class AppletA {
public void start() {
AppletList.register("Stock-trade-applet", this);
...
}
}
另一個 applet 獲取對它的訪問權(quán):
PHP代碼:
public class AppletB {
public void run() {
AppletA tradeApplet =
(AppletA) AppletList.getApplet("Stock-trade-applet");
...
}
}
當(dāng)該 applet 停止運(yùn)行時(shí),您必須緊記在 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: }
要獲得說明如何使用這個類的示例,請?jiān)趨⒖假Y源中下載 exampleCode.zip。
局限性
正如我在前面提到的那樣,必須從同一個 codebase 中加載這些 applet。此外,如果瀏覽器的兩個不同副本正在運(yùn)行,并且 applet 被加載到每個副本中,則 applet 可能無法彼此通信(取決于瀏覽器的版本和設(shè)置),因?yàn)樗鼈兛赡懿辉俟蚕硗粋€運(yùn)行時(shí)環(huán)境。但是,如果是瀏覽器本身衍生出新的瀏覽器窗口,則沒有任何問題。
該技巧已在幾個平臺和幾個瀏覽器版本中通過測試,但在某些配置中每個 applet 的運(yùn)行時(shí)環(huán)境可能是獨(dú)立的。該技巧已在下面的操作系統(tǒng)和瀏覽器組合中通過測試:
Red Hat 6.2: Netscape Navigator 4.73
小結(jié)
這篇技巧說明了能使 applet 彼此通信的一種替代方法。這種方法以 Java API 的 getApplet() 方法不支持的方式工作。這篇技巧中介紹的知識增大了將 applet 作為網(wǎng)站或內(nèi)部網(wǎng)的一部分的可能性 -- 可以用它替代或補(bǔ)充 getApplets 方法。
作者簡介
Tobias Hill 是 Citerus 的創(chuàng)辦者之一,該公司以瑞典為基地,致力于在 Java 平臺上構(gòu)建因特網(wǎng)、內(nèi)部網(wǎng)和外部網(wǎng)系統(tǒng)。Hill 從 1996 年開始用 Java 編程,參與了許多項(xiàng)目,從為自主控制的機(jī)器人編程到開發(fā)在線焰火明信片制作程序等等。
參考資源
* 說明如何使用 AppletList 類的示例:
exampleCode.zip
* Sun 提供的關(guān)于 applet 間通信的 Java 教程:
http://web2.java.sun.com/docs/books...tsonly/iac.html
* Sun 提供的有關(guān) codebase(在其他問題中)的 Java 教程:
http://web2.java.sun.com/docs/books...sonly/html.html
* 查看以前的所有 Java 技巧以及提交您自己的技巧:
http://www.javaworld.com/javatips/j...tips.index.html