Exemplo de API externa: comunicação entre ActionScript e JavaScript em um navegador da Web

Flash Player 9 e posterior, Adobe AIR 1.0 e posterior

Este aplicativo de amostra demonstra técnicas adequadas de comunicação entre o ActionScript e o JavaScript em um navegador da Web, no âmbito de um aplicativo de mensagem instantânea que permite que uma pessoa converse consigo mesma (daí o nome do aplicativo: Introvert IM). As mensagens são enviadas entre um formulário HTML na página da Web e uma interface SWF usando a API externa. As técnicas demonstradas neste exemplo incluem as seguintes:

  • Início adequado da comunicação, verificando se o navegador está preparado para se comunicar antes de estabelecer uma comunicação

  • Verificação da compatibilidade entre o contêiner e a API externa

  • Chamada das funções do JavaScript a partir do ActionScript, transmitindo parâmetros e recebendo valores em resposta

  • Disponibilização dos métodos do ActionScript para serem chamados pelo JavaScript, e realização dessas chamadas

Para obter os arquivos de aplicativo desse exemplo, consulte www.adobe.com/go/learn_programmingAS3samples_flash_br . Os arquivos do aplicativo Introvert IM estão localizados na pasta Amostras/IntrovertIM_HTML. O aplicativo consiste nos seguintes arquivos:

Arquivo

Descrição

IntrovertIMApp.fla

ou

IntrovertIMApp.mxml

O arquivo de aplicativo principal no Flash (FLA) ou Flex (MXML).

com/example/programmingas3/introvertIM/IMManager.as

A classe que estabelece e gerencia a comunicação entre o ActionScript e o contêiner.

com/example/programmingas3/introvertIM/IMMessageEvent.as

Tipo de evento personalizado, enviado pela classe IMManager quando uma mensagem é recebida do contêiner.

com/example/programmingas3/introvertIM/IMStatus.as

Enumeração cujos valores representam diferentes status de "disponibilidade" que podem ser selecionados no aplicativo.

html-flash/IntrovertIMApp.html

ou

html-template/index.template.html

A página HTML do aplicativo para Flash (html-flash/IntrovertIMApp.html) ou o modelo usado para criar a página HTML do contêiner do aplicativo para Adobe Flex (html-template/index.template.html). Esse arquivo contém todas as funções do JavaScript que fazem parte do contêiner do aplicativo.

Preparação da comunicação entre o ActionScript e o navegador

A API externa normalmente é usada para permitir a comunicação dos aplicativos do ActionScript com um navegador da Web. Usando a API externa, os métodos do ActionScript podem chamar o código gravado em JavaScript e vice-versa. Devido à complexidade dos navegadores e ao modo de renderização interna das páginas, não é possível garantir que um documento SWF registrará seus retornos de chamada antes da execução do primeiro JavaScript na página HTML. Por esse motivo, antes de chamar funções no documento SWF a partir do JavaScript, o documento SWF sempre deve chamar a página HTML para notificar se o documento SWF está pronto para aceitar conexões.

Por exemplo, por meio de uma série de etapas realizadas pela classe IMManager, o Introvert IM determina se o navegador está pronto para comunicação e prepara o arquivo SWF para comunicação. A primeira etapa, determinar quando o navegador está pronto para comunicação, acontece no construtor IMManager do seguinte modo:

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

Primeiro, o código verifica se a API externa está realmente disponível no contêiner atual usando a propriedade ExternalInterface.available . Se estiver, o processo de configuração da comunicação é iniciado. Como exceções de segurança e outros erros podem ocorrer ao tentar se comunicar com um aplicativo externo, o código fica entre um bloco try (os blocos catch correspondentes foram omitidos da listagem para facilitar).

O código a seguir chama o método isContainerReady() , listado aqui:

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

O método isContainerReady() , por sua vez, usa o método ExternalInterface.call() para chamar a função isReady() do JavaScript do seguinte modo:

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

A função isReady() simplesmente retorna o valor da variável jsReady . Essa variável é inicialmente false ; assim que o evento onload da página da Web é acionado, o valor da variável muda para true . Em outras palavras, se o ActionScript chamar a função isReady() antes que a página seja carregada, o JavaScript retornará false para ExternalInterface.call("isReady") e, consequentemente, o método isContainerReady() do ActionScript retornará false . Assim que a página for carregada, a função isReady() do JavaScript retornará true , de modo que o método isContainerReady() do ActionScript também retornará true .

De volta ao construtor IMManager, uma de duas coisas acontecem dependendo da disponibilidade do contêiner. Se isContainerReady() retornar true , o código simplesmente chamará o método setupCallbacks() , que concluirá o processo de configuração da comunicação com o JavaScript. Por outro lado, se isContainerReady() retornar false , o processo será basicamente suspenso. Um objeto Timer é criado e suspenso para chamar o método timerHandler() a cada 100 milissegundos do seguinte modo:

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

Sempre que é chamado, o método timerHandler() verifica mais uma vez o resultado do método isContainerReady() . Assim que o contêiner é inicializado, esse método retorna true. Em seguida, o código para o objeto Timer e chama o método setupCallbacks() para finalizar o processo de configuração da comunicação com o navegador.

Exposição dos métodos do ActionScript ao JavaScript

Conforme mostrou o exemplo anterior, assim que o código determina que o navegador está pronto, o método setupCallbacks() é chamado. Esse método prepara o ActionScript para receber chamadas do JavaScript, conforme mostrado a seguir:

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

O método setCallBacks() termina a tarefa de preparação de comunicação com o contêiner chamando ExternalInterface.addCallback() para registrar os dois métodos que estarão disponíveis para serem chamados a partir do JavaScript. Neste código, o primeiro parâmetro - o nome pelo qual o método é conhecido pelo JavaScript ( "newMessage" e "getStatus" ) - é igual ao nome do método no ActionScript. Neste caso, não seria útil usar nomes diferentes, de modo que o mesmo nome foi reutilizado para simplificar. Finalmente, o método ExternalInterface.call() é usado para chamar a função setSWFIsReady() do JavaScript, que avisa ao contêiner que as funções do ActionScript foram registradas.

Comunicação do ActionScript com o navegador

O aplicativo Introvert IM demonstra diversos exemplos de chamada das funções do JavaScript na página do contêiner. No caso mais simples (um exemplo do método setupCallbacks() ), a função setSWFIsReady() do JavaScript é chamada sem transmitir nenhum parâmetro nem receber um valor de retorno:

ExternalInterface.call("setSWFIsReady");

Em outro exemplo do método isContainerReady() , o ActionScript chama a função isReady() e recebe um valor booliano em resposta:

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

Também é possível transmitir parâmetros para as funções do JavaScript usando a API externa. Por exemplo, considere o método sendMessage() da classe IMManager, que é chamado quando o usuário está enviando uma nova mensagem para seu "parceiro de conversa":

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

Novamente, ExternalInterface.call() é usado para chamar a função designada do JavaScript, notificando o navegador sobre a nova mensagem. Além disso, a mensagem propriamente dita é transmitida como um parâmetro adicional para ExternalInterface.call() e, consequentemente, transmitida como parâmetro para a função newMessage() do JavaScript.

Chamada do código do ActionScript a partir do JavaScript

A comunicação é uma rua de mão dupla e o aplicativo Introvert IM não foge à regra. O cliente IM do Flash Player não só chama o JavaScript para enviar mensagens, mas o formulário HTML chama o código do JavaScript para enviar mensagens e solicitar informações do arquivo SWF também. Por exemplo, quando o arquivo SWF avisa o contêiner que terminou de estabelecer o contato e está pronto para se comunicar, a primeira coisa que o navegador faz é chamar o método getStatus() da classe IMManager para recuperar o status de disponibilidade inicial do usuário a partir do cliente IM do SWF. Isso é feito na página da Web, na função updateStatus() , do seguinte modo:

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

O código verifica o valor da variável swfReady , que controla se o arquivo SWF notificou o navegador sobre o registro dos métodos com a classe ExternalInterface. Se o arquivo SWF estiver pronto para receber comunicação, a próxima linha ( var currentStatus = ...) realmente chamará o método getStatus() na classe IMManager. Três coisas acontecem nesta linha de código:

  • A função getSWF() do JavaScript é chamada, retornando uma referência ao objeto do JavaScript que representa o arquivo SWF. O parâmetro transmitido para getSWF() determina qual objeto de navegador é retornado caso haja mais de um arquivo SWF em uma página HTML. O valor transmitido para esse parâmetro deve corresponder ao atributo id da tag object e ao atributo name da tag embed usados para incluir o arquivo SWF.

  • Usando a referência ao arquivo SWF, o método getStatus() é chamado embora seja um método do objeto SWF. Nesse caso, o nome da função “ getStatus ” é usado porque esse é o nome com o qual a função do ActionScript foi registrada com ExternalInterface.addCallback() .

  • O método getStatus() do ActionScript retorna um valor e esse valor é atribuído à variável currentStatus , que é atribuída como conteúdo (a propriedade value ) do campo de texto status .

Nota: Se estiver seguindo o código, você provavelmente percebeu que, no código-fonte da função updateStatus() , a linha de código que chama a função getSWF() é realmente escrita do seguinte modo: var currentStatus = getSWF("${application}").getStatus(). O texto ${application} é um alocador de espaço contido no modelo da página HTML. Quando o Adobe Flash Builder gera a página HTML real para o aplicativo, esse alocador é substituído pelo mesmo texto usado como o atributo id da tag object e o atributo name da tag embed (que corresponde a IntrovertIMApp no exemplo). Este é o valor esperado pela função getSWF() .

A função sendMessage() do JavaScript demonstra a transmissão de um parâmetro como uma função do ActionScript. ( sendMessage() é afunção que foi chamada quando o usuário pressionou o botão Enviar na página HTML.

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

O método newMessage() do ActionScript espera um parâmetro, de modo que a variável message do JavaScript é transmitida para o ActionScript como um parâmetro na chamada do método newMessage() no código do JavaScript.

Detecção do tipo de navegador

Devido às diferenças de acesso ao conteúdo dos navegadores, é importante sempre usar o JavaScript para detectar qual navegados está sendo executado pelo usuário e para acessar o filme de acordo com a sintaxe específica do navegador, usando o objeto de janela ou documento, como mostra a função getSWF() do JavaScript neste exemplo:

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

Se o seu script não detectar o tipo de navegador do usuário, o usuário poderá observar um comportamento inesperado ao reproduzir arquivos SWF em um contêiner HTML.