Voorbeeld van externe API: communicatie tussen ActionScript en JavaScript in een webbrowser

Flash Player 9 of hoger, Adobe AIR 1.0 of hoger

Deze voorbeeldtoepassing toont geschikte technieken die communicatie tussen ActionScript en JavaScript in een webbrowser mogelijk maken, binnen de context van een Instant Messaging-toepassing waarmee een gebruiker kan chatten met zichzelf (vandaar de naam van de toepassing: Introvert IM). Met behulp van de externe API worden berichten verzonden tussen een HTML-formulier in de webpagina en een SWF-interface. De technieken die in dit voorbeeld worden getoond, zijn onder meer:

  • communicatie op correcte wijze starten, door te controleren of de browser klaar is voor communicatie alvorens communicatie in te stellen.

  • Controleren of de container de externe API ondersteunt.

  • JavaScript-functies aanroepen vanuit ActionScript, parameters doorgeven en als reactie waarden ontvangen.

  • ActionScript-methoden beschikbaar maken om te worden aangeroepen door JavaScript en deze aanroepen uitvoeren.

Zie www.adobe.com/go/learn_programmingAS3samples_flash_nl als u de toepassingsbestanden voor dit voorbeeld wilt downloaden. De bestanden voor de Introvert IM-toepassing vindt u in de map Samples/IntrovertIM_HTML. De toepassing bestaat uit de volgende bestanden:

Bestand

Beschrijving

IntrovertIMApp.fla

of

IntrovertIMApp.mxml

Het hoofdtoepassingsbestand voor Flash (FLA) of Flex (MXML).

com/example/programmingas3/introvertIM/IMManager.as

De klasse die communicatie tussen ActionScript en de container tot stand brengt en deze beheert.

com/example/programmingas3/introvertIM/IMMessageEvent.as

Type aangepaste gebeurtenis, verzonden door de klasse IMManager wanneer een bericht wordt ontvangen door de container.

com/example/programmingas3/introvertIM/IMStatus.as

Opsomming waarvan de waarden de verschillende waarden vertegenwoordigen van de status ‘beschikbaarheid’ die binnen deze toepassing kunnen worden geselecteerd.

html-flash/IntrovertIMApp.html

of

html-template/index.template.html

De HTML-pagina voor de Flash-toepassing (html-flash/IntrovertIMApp.html) of de sjabloon die wordt gebruikt om de HTML-containerpagina voor de toepassing voor Adobe Flex (html-template/index.template.html) te maken. Dit bestand bevat alle JavaScript-functies die het containergedeelte van de toepassing vormen.

Voorbereiden op communicatie via de ActionScript-browser

Een van de meest gebruikte toepassingen van de externe API is om ActionScript-toepassingen te laten communiceren met een webbrowser. ActionScript-methoden kunnen met de externe API codes aanroepen die zijn geschreven in JavaScript en omgekeerd. Gezien de complexiteit van browsers en de wijze waarop deze pagina’s intern renderen, kan niet worden gegarandeerd dat een SWF-document zijn callbacks registreert voordat het eerste JavaScript op de HTML-pagina wordt uitgevoerd. Om deze reden moet uw SWF-document, voordat hierin functies vanuit JavaScript worden aangeroepen, altijd eerst de HTML-pagina aanroepen en melden dat het SWF-document gereed is om verbindingen te accepteren.

De Introvert IM bepaalt bijvoorbeeld aan de hand van een reeks stappen die worden uitgevoerd door de klasse IMManager, of de browser gereed is voor communicatie en bereidt het SWF-bestand voor op communicatie. De eerste stap, waarin wordt bepaald of de browser gereed is voor communicatie, vindt als volgt plaats in de constructor 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."); 
    } 
}

Ten eerste controleert de code met behulp van de eigenschap ExternalInterface.available of de externe API beschikbaar is in de huidige container. Als dit het geval is, start de code het proces waarbij communicatie wordt ingesteld. Omdat er zich uitzonderingen en andere fouten kunnen voordoen wanneer u communicatie tot stand probeert te brengen met een externe toepassing, is de code opgenomen in een blok try (voor een beknopte lijst zijn de overeenkomstige blokken catch weggelaten).

De volgende code roept de methode isContainerReady() aan:

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

De methode isContainerReady() maakt op zijn beurt gebruik van de methode ExternalInterface.call() om de JavaScript-functie isReady() als volgt aan te roepen:

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

De functie isReady() retourneert alleen de waarde van de variabele jsReady. Deze variabele is aanvankelijk false; nadat de gebeurtenis onload van de webpagina geactiveerd is, wordt de waarde van de variabele gewijzigd in true. Met andere woorden: als ActionScript de functie isReady() aanroept voordat de pagina is geladen, retourneert JavaScript false naar ExternalInterface.call("isReady"); als gevolg hiervan retourneert de methode ActionScript isContainerReady()false. Nadat de pagina is geladen, retourneert de JavaScript-functie isReady()true, zodat de ActionScript-methode isContainerReady() eveneens true retourneert.

In de constructor IMManager gebeurt vervolgens één van de volgende twee dingen, afhankelijk van de gereedheid van de container. Als isContainerReady()true retourneert, roept de code alleen de methode setupCallbacks() aan, die het proces waarbij communicatie met JavaScript wordt ingesteld, afrondt. Als daarentegen isContainerReady()false retourneert, wordt het proces gepauzeerd. Er wordt een object Timer gemaakt dat de methode timerHandler() iedere 100 milliseconden als volgt aanroept:

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

Steeds als de methode timerHandler() wordt aangeroepen, controleert deze opnieuw het resultaat van de methode isContainerReady(). Nadat de container is geïnitialiseerd, retourneert deze methode true. De code stopt daarna de Timer en roept de methode setupCallbacks() aan om het proces te beëindigen waarbij communicatie met de browser wordt ingesteld.

ActionScript-methoden beschikbaar maken voor JavaScript

Zoals uit het vorige voorbeeld blijkt, wordt de methode setupCallbacks() aangeroepen, nadat de code heeft vastgesteld dat de browser gereed is. Deze methode bereidt ActionScript voor om aanroepen te ontvangen vanuit JavaScript, zoals hier wordt getoond:

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

De methode setCallBacks() voltooit de taak waarbij communicatie met de container wordt voorbereid, door ExternalInterface.addCallback() aan te roepen en de twee methoden te registreren die beschikbaar zijn om vanuit JavaScript te worden aangeroepen. In deze code is de eerste parameter (de naam waaronder de methode bekend is bij JavaScript, "newMessage" en "getStatus") gelijk aan de naam van de methode in ActionScript. (In dit geval leverde het geen voordeel op om verschillende namen te gebruiken, zodat voor het gemak dezelfde naam opnieuw werd gebruikt.) Tot slot wordt de methode ExternalInterface.call() gebruikt om de JavaScript-functie setSWFIsReady() aan te roepen, die aan de container meldt dat de ActionScript-functies zijn geregistreerd.

Communicatie vanuit ActionScript naar de browser

De toepassing Introvert IM toont een reeks voorbeelden waarbij JavaScript-functies in de containerpagina worden aangeroepen. In het meest eenvoudige geval (een voorbeeld van de methode setupCallbacks()) wordt de functie JavaScript setSWFIsReady() aangeroepen zonder dat er parameters worden doorgegeven of er als reactie een waarde wordt ontvangen:

ExternalInterface.call("setSWFIsReady");

In een ander voorbeeld van de methode isContainerReady() roept ActionScript de functie isReady() aan en ontvangt als reactie een Booleaanse waarde:

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

U kunt ook parameters doorgeven aan JavaScript-functies die gebruik maken van de externe API. Zie bijvoorbeeld de methode sendMessage() van de klasse IMManager, die wordt aangeroepen wanneer de gebruiker een nieuw bericht verstuurt naar zijn of haar ‘conversatiepartner’:

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

Ook hier wordt ExternalInterface.call() weer gebruikt om de opgegeven JavaScript-functie aan te roepen, waarbij het nieuwe bericht aan de browser wordt doorgegeven. De melding zelf wordt doorgegeven als aanvullende parameter aan ExternalInterface.call() en wordt als gevolg hiervan doorgegeven als parameter aan de JavaScript-functie newMessage().

ActionScript-code vanuit JavaScript aanroepen

Communicatie werkt altijd in twee richtingen en de toepassing Introvert IM vormt hierop geen uitzondering. De IM client van Flash Player roept niet alleen JavaScript aan voor het verzenden van berichten, maar het HTML-formulier roept de JavaScript-code aan om zowel berichten te verzenden als om informatie van het SWF-bestand op te vragen. Wanneer het SWF-bestand bijvoorbeeld aan de container meldt dat het contact tot stand heeft gebracht en gereed is om te communiceren, roept de browser eerst de methode getStatus() van de klasse IMManager aan, om de beschikbaarheidsstatus van de gebruiker op te halen uit de SWF IM-client. Dit vindt als volgt plaats op de webpagina in de functie updateStatus():

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

De code controleert de waarde van de variabele swfReady, die bijhoudt of het SWF-bestand aan de browser heeft doorgegeven dat de methoden ervan zijn geregistreerd met de klasse ExternalInterface. Als het SWF-bestand gereed is communicatie te ontvangen, roept de volgende regel (var currentStatus = ...) de methode getStatus() daadwerkelijk in de klasse IMManager aan. In deze coderegel gebeuren drie dingen:

  • De JavaScript-functie getSWF() wordt aangeroepen, die een verwijzing naar het JavaScript-object retourneert die het SWF-bestand vertegenwoordigt. De parameter die is doorgegeven aan getSWF() bepaalt welk browserobject wordt geretourneerd wanneer er zich meer dan één SWF-bestand in een HTML-pagina bevindt. De waarde die wordt doorgegeven aan deze parameter moet overeenkomen met het kenmerk id van de tag object en het kenmerk name van de tag embed die wordt gebruikt om het SWF-bestand in te sluiten.

  • De methode getStatus() wordt met de verwijzing naar het SWF-bestand aangeroepen, hoewel dit een methode van het SWF-object is. In dit geval wordt gebruik gemaakt van de functienaam "getStatus", omdat dit de naam is waarmee de ActionScript-functie met ExternalInterface.addCallback() is geregistreerd.

  • De ActionScript-methode getStatus() retourneert een waarde en deze waarde wordt toegewezen aan de variabele currentStatus, die daarop wordt toegewezen aan de inhoud (de eigenschap value) van het tekstveld status.

Opmerking: Als u de code volgt, is u waarschijnlijk al opgevallen dat in de broncode voor de updateStatus()-functie, de coderegel die de getSWF()-functie aanroept, als volgt is geschreven: var currentStatus = getSWF("${application}").getStatus(De tekst ${application} is een plaatsaanduiding in het HTML-paginasjabloon; wanneer Adobe Flash Builder de werkelijke HTML-pagina voor de toepassing genereert, wordt deze plaatsaanduidingstekst vervangen door dezelfde tekst die wordt gebruikt als het id-kenmerk van het object-tag en het name-kenmerk van de embed-tag (IntrovertIMApp in het voorbeeld). Dat is de waarde die wordt verwacht door de functie getSWF().

De JavaScript-functie sendMessage() laat zien hoe een parameter wordt doorgegeven aan een ActionScript-functie. (sendMessage() is de functie die wordt aangeroepen wanneer de gebruiker op de knop Verzenden drukt op de HTML-pagina.)

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

De ActionScript-methode newMessage() verwacht één parameter, zodat de JavaScript-variabele message wordt doorgegeven aan ActionScript door deze te gebruiken als een parameter in de methodeaanroep newMessage() in de JavaScript-code.

Type browser detecteren

De wijze waarop browsers inhoud benaderen, kan verschillen. Het is daarom belangrijk dat JavaScript altijd wordt gebruikt om te detecteren welke browser de gebruiker uitvoert en om de film met behulp van het venster- of documentobject te benaderen volgens de browserspecifieke syntaxis, zoals wordt weergegeven in de JavaScript-functie getSWF() in dit voorbeeld:

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

Als uw script het type browser van de gebruiker niet detecteert, vertonen de SWF-bestanden tijdens het afspelen in een HTML-container mogelijk een ander gedrag dan de gebruiker verwacht.