外部 API 範例:使用瀏覽器在 ActionScript 和 JavaScript 之間通訊

Flash Player 9 以及更新的版本,Adobe AIR 1.0 以及更新的版本

這個樣本應用程式將示範在允許與他人交談的即時傳訊應用程式環境中 (應用程式的名稱是 Introvert IM),於網頁瀏覽器內的 ActionScript 與 JavaScript 之間進行通訊的正確技巧。所有訊息都是使用外部 API,在網頁的 HTML 表單與 SWF 介面之間傳送。這個範例將示範的技巧包括下列各項:

  • 在設定通訊之前先確認瀏覽器已經準備開始通訊,以便正確地起始通訊

  • 檢查容器是否支援外部 API

  • 透過 ActionScript 呼叫 JavaScript 函數、傳遞參數以及接收回應值

  • 讓 JavaScript 可以呼叫 ActionScript 方法,並執行這些呼叫

若要取得此樣本的應用程式檔案,請參閱 www.adobe.com/go/learn_programmingAS3samples_flash_tw 。您可以在 Samples/IntrovertIM_HTML 檔案夾中找到 Introvert IM 應用程式檔案,此應用程式是由下列檔案組成:

檔案

說明

IntrovertIMApp.fla

IntrovertIMApp.mxml

主應用程式檔案,在 Flash 中為 FLA,在 Flex 中為 MXML。

com/example/programmingas3/introvertIM/IMManager.as

類別,建立與管理 ActionScript 和容器之間的通訊。

com/example/programmingas3/introvertIM/IMMessageEvent.as

自訂的事件類型,從容器接收訊息後,由 IMManager 類別將它送出。

com/example/programmingas3/introvertIM/IMStatus.as

列舉項目,它的值表示可以在應用程式中選取的各種「可用性」狀態值。

html-flash/IntrovertIMApp.html

html-template/index.template.html

Flash 應用程式的 HTML 網頁 (html-flash/IntrovertIMApp.html) 或是用來建立 Adobe Flex 應用程式的容器 HTML 網頁的範本 (html-template/index.template.html)。這個檔案包含所有構成應用程式容器部分的 JavaScript 函數。

準備開始在 ActionScript 與瀏覽器之間進行通訊

外部 API 最常見的用途之一,便是允許 ActionScript 應用程式與網頁瀏覽器進行通訊。利用外部 API,ActionScript 方法可以呼叫以 JavaScript 撰寫的程式碼,反之亦然。由於瀏覽器的複雜性以及在內部呈現頁面的不同方式,因此在執行 HTML 網頁的第一個 JavaScript 之前,並無法保證 SWF 文件可以註冊它的回呼。所以,透過 JavaScript 呼叫 SWF 文件中的函數之前,您的 SWF 文件永遠都要先呼叫 HTML 網頁,通知它 SWF 文件已經準備開始接收連線。

例如,透過由 IMManager 類別執行的一系列步驟,Introvert IM 就可以判斷瀏覽器是否已經準備開始進行通訊並準備通訊用的 SWF 檔。第一個步驟是判斷瀏覽器準備開始進行通訊的時間,此步驟會在 IMManager 建構函式中執行,如下所示:

public function IMManager(initialStatus:IMStatus) 
{ 
    _status = initialStatus; 
 
    // Check if the container is able to use the external API. 
    if (ExternalInterface.available) 
    { 
        try 
        { 
            // This calls the isContainerReady() method, which in turn calls 
            // the container to see if Flash Player has loaded and the container 
            // is ready to receive calls from the SWF. 
            var containerReady:Boolean = isContainerReady(); 
            if (containerReady) 
            { 
                // If the container is ready, register the SWF's functions. 
                setupCallbacks(); 
            } 
            else 
            { 
                // If the container is not ready, set up a Timer to call the 
                // container at 100ms intervals. Once the container responds that 
                // it's ready, the timer will be stopped. 
                var readyTimer:Timer = new Timer(100); 
                readyTimer.addEventListener(TimerEvent.TIMER, timerHandler); 
                readyTimer.start(); 
            } 
        } 
        ... 
    } 
    else 
    { 
        trace("External interface is not available for this container."); 
    } 
}

首先,程式碼會使用 ExternalInterface.available 屬性,檢查目前的容器是否可以使用外部 API。如果可以使用,它就開始進行設定通訊的程序。由於當嘗試與外部應用程式進行通訊時,有可能會發生安全性例外和其它錯誤,因此程式碼會位於 try 區塊 (為保持程式碼簡潔,清單中會省略對應的 catch 區塊) 中。

程式碼接著會呼叫 isContainerReady() 方法,如下所示:

private function isContainerReady():Boolean 
{ 
    var result:Boolean = ExternalInterface.call("isReady"); 
    return result; 
}

然後 isContainerReady() 會使用 ExternalInterface.call() 方法呼叫 JavaScript 函數 isReady() ,如下所示:

<script language="JavaScript"> 
<!-- 
// ------- Private vars ------- 
var jsReady = false; 
... 
// ------- functions called by ActionScript ------- 
// called to check if the page has initialized and JavaScript is available 
function isReady() 
{ 
    return jsReady; 
} 
... 
// called by the onload event of the <body> tag 
function pageInit() 
{ 
    // Record that JavaScript is ready to go. 
    jsReady = true; 
} 
... 
//--> 
</script>

isReady() 函數只會傳回 jsReady 變數的值。此變數的起始值是 false ,當觸發網頁的 onload 事件後,此變數的值才會變成 true 。換句話說,如果 ActionScript 在載入網頁前就呼叫 isReady() 函數,JavaScript 會將 false 傳回給 ExternalInterface.call("isReady") ,而使 ActionScript 的 isContainerReady() 方法傳回 false 。網頁一經載入後,JavaScript isReady() 函數就會傳回 true ,因此 ActionScript isContainerReady() 方法也會傳回 true

回到 IMManager 建構函式中,根據容器的準備狀態會發生下列兩種狀況的其中一種狀況。如果 isContainerReady() 傳回 true ,則程式碼只會呼叫 setupCallbacks() 方法,使用 JavaScript 來完成設定通訊的程序;另一種情況是,如果 isContainerReady() 傳回 false ,則會將程序暫停,並建立 Timer 物件,使其每 100 毫秒就呼叫一次 timerHandler() 方法,如下所示:

private function timerHandler(event:TimerEvent):void 
{ 
    // Check if the container is now ready. 
    var isReady:Boolean = isContainerReady(); 
    if (isReady) 
    { 
        // If the container has become ready, we don't need to check anymore, 
        // so stop the timer. 
        Timer(event.target).stop(); 
        // Set up the ActionScript methods that will be available to be 
        // called by the container. 
        setupCallbacks(); 
    } 
}

每當呼叫 timerHandler() 方法時,它會再檢查一次 isContainerReady() 方法的結果。一旦容器經過初始化,這個方法就會傳回 true 。然後程式碼就會停止執行 Timer 並呼叫 setupCallbacks() 方法,完成瀏覽器的通訊設定程序。

將 ActionScript 方法公開給 JavaScript

如上一個範例中所示,只要程式碼判斷瀏覽器已經完成準備,就會呼叫 setupCallbacks() 方法。這個方法會開始準備讓 ActionScript to 接收來自 JavaScript 的呼叫,如下所示:

private function setupCallbacks():void 
{ 
    // Register the SWF client functions with the container 
    ExternalInterface.addCallback("newMessage", newMessage); 
    ExternalInterface.addCallback("getStatus", getStatus); 
    // Notify the container that the SWF is ready to be called. 
    ExternalInterface.call("setSWFIsReady"); 
}

setCallBacks() 方法會呼叫 ExternalInterface.addCallback() ,註冊將可以透過 JavaScript 呼叫的兩個方法,以完成與容器通訊的準備工作。在這段程式碼中,第一個參數 (JavaScript ( "newMessage" "getStatus" ) 知道的方法名稱) 和 ActionScript 中的方法會使用相同的名稱 (在這種情況下,使用不同的名稱並沒有任何益處,因此為簡化起見,會重複使用相同的名稱)。最後,再使用 ExternalInterface.call() 方法呼叫 JavaScript 函數 setSWFIsReady() ,讓容器知道已經完成 ActionScript 函數的註冊作業。

在 ActionScript 與瀏覽器之間進行通訊

Introvert IM 應用程式將示範一些如何呼叫容器網頁中 JavaScript 函數的範例。在最簡單的範例 ( setupCallbacks() 方法中的範例) 中,呼叫 JavaScript 函數 setSWFIsReady() 時並沒有傳遞任何參數或接收傳回的值:

ExternalInterface.call("setSWFIsReady");

而在另一個來自 isContainerReady() 方法中的範例,ActionScript 會呼叫 isReady() 函數並接收回應的 Boolean 值:

var result:Boolean = ExternalInterface.call("isReady");

您也可以使用外部 API,將這些參數傳遞給 JavaScript 函數。例如,您可以考慮使用 IMManager 類別的 sendMessage() 方法,當使用者傳送新的訊息給他的「交談夥伴」時,就會呼叫這個方法:

public function sendMessage(message:String):void 
{ 
    ExternalInterface.call("newMessage", message); 
}

同樣地,會使用 ExternalInterface.call() 呼叫指定的 JavaScript 函數,通知瀏覽器有新的訊息送達。此外,訊息本身會做為額外參數傳遞給 ExternalInterface.call() ,因此它也會做為參數傳遞給 JavaScript 函數 newMessage()

透過 JavaScript 呼叫 ActionScript 程式碼

所謂通訊,應該就像一條雙線道一樣,Introvert IM 應用程式也不例外。不僅 Flash Player IM 用戶端會呼叫 JavaScript 來傳送訊息,HTML 表單也會呼叫 JavaScript 程式碼來傳送訊息以及要求來自 SWF 檔的資訊。例如,當 SWF 檔通知容器它已經完成建立聯絡通道且已準備進行通訊時,瀏覽器首先會呼叫 IMManager 類別的 getStatus() 方法,從 SWF IM 用戶端擷取第一個使用者的可用性狀態。這項作業是在網頁內的 updateStatus() 函數中完成,如下所示:

<script language="JavaScript"> 
... 
function updateStatus() 
{ 
    if (swfReady) 
    { 
        var currentStatus = getSWF("IntrovertIMApp").getStatus(); 
        document.forms["imForm"].status.value = currentStatus; 
    } 
} 
... 
</script>

程式碼會檢查 swfReady 變數的值,以追蹤 SWF 檔是否已經通知瀏覽器它已經使用 ExternalInterface 類別註冊它的方法。如果 SWF 檔已經準備要接收通訊內容,下一行程式碼 ( var currentStatus = ...) 就會實際呼叫 IMManager 類別中的 getStatus() 方法。這一行程式碼將執行三項作業:

  • 呼叫 getSWF() JavaScript 函數,傳回代表 SWF 檔之 JavaScript 物件的參考。當 HTML 網頁中有一個以上的 SWF 檔時,傳遞給 getSWF() 的參數會判斷應該傳回哪一個瀏覽器物件。傳遞給這個參數的值必須與用來內嵌 SWF 檔之 object 標籤的 id 特質和 embed 標籤的 name 特質相符。

  • 使用 SWF 檔的參考時,呼叫 getStatus() 方法就等於是呼叫 SWF 物件的方法。在這種情況下,會使用函數名稱 getStatus ,因為這是使用 ExternalInterface.addCallback() 註冊 ActionScript 函數時所使用的名稱。

  • getStatus() ActionScript 方法會傳回值,並且將值指派給 currentStatus 變數,做為 status 文字欄位的內容 ( value 屬性)。

備註: 看完上述程式碼時,您可能已經注意到,在 updateStatus() 函數的原始碼中,呼叫 getSWF() 函數的程式碼實際上撰寫如下:var currentStatus = getSWF("${application}").getStatus(),其中 ${application} 文字是 HTML 網頁範本中的預留位置。當 Adobe Flash Builder 為該程式產生實際的 HTML 網頁時,預留位置文字會由當做 object 標籤之 id 特質以及 embed 標籤之 name 特質 (在此範例中為 IntrovertIMApp ) 的相同文字所取代。這就是 getSWF() 函數需要的值。

sendMessage() JavaScript 函數會示範如何將參數傳遞給 ActionScript 函數 ( sendMessage() 是當使用者按下 HTML 網頁上的「傳送」按鈕時就會呼叫的函數)。

<script language="JavaScript"> 
... 
function sendMessage(message) 
{ 
    if (swfReady) 
    { 
        ... 
        getSWF("IntrovertIMApp").newMessage(message); 
    } 
} 
... 
</script>

newMessage() ActionScript 方法需要使用一個參數,讓 JavaScript message 變數可以當做 JavaScript 程式碼之 newMessage() 方法呼叫中的參數,傳遞給 ActionScript。

偵測瀏覽器類型

由於瀏覽器存取內容的方式各有不同,因此一律透過 JavaScript 使用視窗或文件物件來偵測使用者執行的瀏覽器類型,以及根據瀏覽器指定的語法來存取影片非常重要,如下列範例中的 getSWF() JavaScript 函數所示:

<script language="JavaScript"> 
... 
function getSWF(movieName) 
{ 
    if (navigator.appName.indexOf("Microsoft") != -1) 
    { 
        return window[movieName]; 
    } 
    else 
    { 
        return document[movieName]; 
    } 
} 
... 
</script>

如果指令碼無法偵測使用者的瀏覽器類型,當使用者播放 HTML 容器中的 SWF 檔時,便可能會發生無法預期的行為。