Esempio di API esterna: comunicazione tra ActionScript e JavaScript in un browser Web

Flash Player 9 e versioni successive, Adobe AIR 1.0 e versioni successive

Questo esempio di applicazione mostra le tecniche appropriate per abilitare la comunicazione tra ActionScript e JavaScript in un browser Web, nel contesto di un'applicazione di Instant Messaging che consente a un utente di inviare messaggi chat a se stesso (da cui il nome dell'applicazione: Introvert IM). I messaggi vengono scambiati tra un form HTML nella pagina Web e un'interfaccia SWF che utilizza l'API esterna. La tecnica dimostrata nell'esempio consente di:

  • Avviare correttamente la comunicazione dopo aver verificato che il browser sia pronto per comunicare prima di impostare la comunicazione

  • Verificare che il contenitore supporti l'API esterna

  • Chiamare le funzioni JavaScript da ActionScript, passare i parametri e ricevere dei valori in risposta

  • Rendere i metodi ActionScript disponibili alle chiamate di JavaScript ed eseguire tali chiamate

Per ottenere i file dell'applicazione per questo esempio, visitate la pagina www.adobe.com/go/learn_programmingAS3samples_flash_it . I file dell'applicazione Introvert IM sono disponibili nella cartella Samples/IntrovertIM_HTML. L'applicazione è composta dai seguenti file:

File

Descrizione

IntrovertIMApp.fla

o

IntrovertIMApp.mxml

Il file principale dell'applicazione per Flash (FLA) o Flex (MXML).

com/example/programmingas3/introvertIM/IMManager.as

La classe che attiva e gestisce la comunicazione tra ActionScript e il contenitore.

com/example/programmingas3/introvertIM/IMMessageEvent.as

Il tipo di evento personalizzato, inviato dalla classe IMManager quando viene ricevuto un messaggio dal contenitore.

com/example/programmingas3/introvertIM/IMStatus.as

Un'enumerazione i cui valori rappresentano i diversi valori dello stato di “disponibilità” che è possibile selezionare nell'applicazione.

html-flash/IntrovertIMApp.html

o

html-template/index.template.html

La pagina HTML per l'applicazione per Flash (html-flash/IntrovertIMApp.html) o il modello utilizzato per creare la pagina HTML contenitore per l'applicazione per Adobe Flex (html-template/index.template.html). Questo file contiene tutte le funzioni JavaScript che compongono la parte contenitore dell'applicazione.

Preparazione di una comunicazione ActionScript-browser

L'API esterna viene molto spesso utilizzata per consentire alle applicazioni ActionScript di comunicare con un browser Web. Grazie all'API esterna, i metodi ActionScript possono chiamare il codice scritto in JavaScript e viceversa. In virtù della complessità dei browser e del modo in cui effettuano il rendering interno delle pagine, non è possibile garantire che un documento SWF registri le proprie funzioni di callback prima che venga eseguito il primo codice JavaScript della pagina HTML. Per tale motivo, prima di chiamare le funzioni nel documento SWF da JavaScript, il documento SWF deve sempre chiamare la pagina HTML per notificarle che il documento SWF è pronto per accettare le connessioni.

Attraverso una serie di operazioni eseguite, ad esempio, dalla classe IMManager, l'applicazione Introvert IM determina se il browser è pronto per la comunicazione e prepara il documento SWF di conseguenza. La prima operazione, ovvero determinare quando il browser è pronto per la comunicazione, si svolge nella funzione di costruzione IMManager, come indicato di seguito:

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."); 
    } 
}

Innanzitutto, il codice verifica se l'API esterna è almeno disponibile nel contenitore corrente utilizzando la proprietà ExternalInterface.available . In caso affermativo, inizia il processo di impostazione della comunicazione. Poiché quando si tenta la comunicazione con un'applicazione esterna possono verificarsi delle eccezioni di sicurezza e altri errori, il codice viene racchiuso in un blocco try (i blocchi catch corrispondenti sono stati omessi dall'elenco per ragioni di spazio).

Il codice successivo chiama il metodo isContainerReady() , rappresentato qui:

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

Il metodo isContainerReady() utilizza a propria volta il metodo ExternalInterface.call() per chiamare la funzione JavaScript isReady() , come indicato di seguito:

<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>

La funzione isReady() restituisce semplicemente il valore della variabile jsReady . Tale variabile inizialmente è false ; quando l'evento onload della pagina Web è stato attivato, il valore della variabile diventa true . In altre parole, se ActionScript chiama la funzione isReady() prima che la pagina sia stata caricata, JavaScript restituisce false a ExternalInterface.call("isReady") , e di conseguenza il metodo ActionScript isContainerReady() restituisce false . Una volta caricata la pagina, la funzione JavaScript isReady() restituisce true , pertanto anche il metodo ActionScript isContainerReady() restituisce true .

Tornando alla funzione di costruzione IMManager, a seconda della prontezza del contenitore può verificarsi una delle due condizioni seguenti. Se isContainerReady() restituisce true , il codice chiama semplicemente il metodo setupCallbacks() , che completa il processo di impostazione della comunicazione con JavaScript. Se invece isContainerReady() restituisce false , il processo viene essenzialmente sospeso. Viene creato un oggetto Timer a cui viene ordinato di chiamare il metodo timerHandler() ogni 100 millisecondi, come indicato di seguito:

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(); 
    } 
}

Ogni volta che il metodo timerHandler() viene chiamato, verifica ancora una volta il risultato del metodo isContainerReady() . Una volta inizializzato il contenitore, tale metodo restituisce il valore true. A quel punto, il codice arresta il timer e chiama il metodo setupCallbacks() per completare il processo di impostazione della comunicazione con il browser.

Esposizione dei metodi ActionScript a JavaScript

Come illustrato nell'esempio precedente, quando il codice determina che il browser è pronto, viene chiamato il metodo setupCallbacks() . Questo metodo prepara ActionScript alla ricezione delle chiamate da JavaScript, come illustrato di seguito:

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"); 
}

Il metodo setCallBacks() conclude la preparazione alla comunicazione chiamando ExternalInterface.addCallback() per registrare i due metodi che saranno disponibili per le chiamate da JavaScript. In questo codice, il primo parametro (il nome con cui il metodo è conosciuto da JavaScript, ovvero "newMessage" e "getStatus" ) è uguale al nome del metodo in ActionScript (nel caso dell'esempio, poiché sarebbe stato ininfluente indicare nomi diversi, per semplicità è stato utilizzato lo stesso nome). Infine, viene utilizzato il metodo ExternalInterface.call() per chiamare la funzione JavaScript setSWFIsReady() , che notifica al contenitore che le funzioni ActionScript sono state registrate.

Comunicazione da ActionScript al browser

L'applicazione Introvert IM dimostra una serie di esempi di chiamate a funzioni JavaScript nella pagina contenitore. Nel caso più semplice (un esempio tratto dal metodo setupCallbacks() ), la funzione JavaScript setSWFIsReady() viene chiamata senza passare alcun parametro o ricevere alcun valore:

ExternalInterface.call("setSWFIsReady");

In un altro esempio tratto dal metodo isContainerReady() , ActionScript chiama la funzione isReady() e riceve come risposta un valore booleano:

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

Potete passare parametri alle funzioni JavaScript anche utilizzando l'API esterna. Considerate, ad esempio, il metodo sendMessage() della classe IMManager, che viene chiamato quando l'utente sta inviando un nuovo messaggio al proprio interlocutore:

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

Anche in questo caso, si utilizza ExternalInterface.call() per chiamare la funzione JavaScript designata, notificando al browser il nuovo messaggio. Inoltre, il messaggio stesso viene passato come parametro aggiuntivo a ExternalInterface.call() , e di conseguenza viene passato come parametro alla funzione JavaScript newMessage() .

Chiamate al codice ActionScript da JavaScript

Una comunicazione dovrebbe essere sempre bidirezionale, e l'applicazione Introvert IM non fa eccezione. Non solo il client IM di Flash Player chiama JavaScript per inviare i messaggi, ma il form HTML chiama il codice JavaScript anche per inviare messaggi e chiedere informazioni al file SWF. Quando, ad esempio, il file SWF notifica al contenitore che il contatto è stato stabilito ed è pronto per comunicare, come prima cosa il browser chiama il metodo getStatus() della classe IMManager per recuperare lo stato di disponibilità iniziale dell'utente dal client IM SWF. Questo avviene in una pagina Web, nella funzione updateStatus() , nel modo indicato di seguito:

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

Il codice verifica il valore della variabile swfReady , che rileva se il file SWF ha notificato il browser che ne ha registrato i metodi con la classe ExternalInterface. Se il file SWF è pronto per ricevere la comunicazione, la riga successiva ( var currentStatus = ...) chiama di fatto il metodo getStatus() nella classe IMManager. In questa riga di codice vengono eseguite tre operazioni:

  • Viene chiamata la funzione JavaScript getSWF() , che restituisce un riferimento all'oggetto JavaScript che rappresenta il file SWF. Il parametro passato a getSWF() determina quale oggetto browser viene restituito nel caso in cui in una stessa pagina HTML sia presente più di un solo file SWF. Il valore passato a tale parametro deve corrispondere all'attributo id del tag object e all'attributo name del tag embed utilizzati per includere il file SWF.

  • Utilizzando il riferimento al file SWF, viene chiamato il metodo getStatus() come se fosse un metodo dell'oggetto SWF. In questo caso, viene utilizzato il nome di funzione “ getStatus ” perché è il nome con cui la funzione ActionScript viene registrata mediante ExternalInterface.addCallback() .

  • Il metodo ActionScript getStatus() restituisce un valore. Tale valore viene assegnato alla variabile currentStatus , che viene successivamente assegnata come contenuto (la proprietà value ) del campo di testo status .

Nota: se state seguendo il codice, avrete notato che nel codice sorgente della funzione updateStatus() , la riga di codice che chiama la funzione getSWF() è scritta nel seguente modo: var currentStatus = getSWF("${application}").getStatus(); il testo ${application} è un segnaposto all'interno della pagina template HTML; quando Adobe Flash Builder genera la pagina HTML effettiva per l'applicazione, questo segnaposto viene sostituito dallo stesso testo utilizzato come attributo id del tag object e dall'attributo nome del tag embed (nell'esempio IntrovertIMApp ). Questo è il valore previsto dalla funzione getSWF() .

La funzione JavaScript sendMessage() dimostra il passaggio di un parametro a una funzione ActionScript ( sendMessage() è la funzione che viene chiamata quando l'utente preme il pulsante Invia presente nella pagina HTML).

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

Il metodo ActionScript newMessage() prevede un solo parametro, pertanto la variabile JavaScript message viene passata ad ActionScript utilizzandola come parametro nella chiamata al metodo newMessage() nel codice JavaScript.

Rilevamento del tipo di browser

A causa delle differenze nel modo in cui i browser accedono ai contenuti, è importante utilizzare sempre JavaScript per rilevare quale browser è utilizzato dall'utente e per accedere al filmato in base alla sintassi specifica del browser, utilizzando l'oggetto window o document, come mostrato nella funzione JavaScript getSWF() dell'esempio seguente:

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

Se lo script non rileva il tipo di browser dell'utente, quest'ultimo potrebbe sperimentare un comportamento imprevisto durante la riproduzione dei file SWF in un contenitore HTML.