Пример внешнего API: связь между ActionScript и JavaScript в веб-обозревателе

Flash Player 9 и более поздних версий, Adobe AIR 1.0 и более поздних версий

Этот образец приложения демонстрирует применение подходящих методов обмена данными между ActionScript и JavaScript в веб-обозревателе в контексте приложения для мгновенного обмена сообщениями, которое позволяет человеку участвовать в чате с самим собой (отсюда и имя приложения: Introvert IM). Пересылка сообщений между формой HTML на веб-странице и интерфейсом SWF выполняется с помощью внешнего API. В этом примере показаны, например, следующие методы:

  • Правильная инициация обмена данными путем проверки готовности обозревателя к связи перед установкой подключения

  • Проверка наличия у контейнера поддержки внешнего API

  • Вызов функций JavaScript из ActionScript, передача параметров и получение в ответ значений

  • Предоставление возможности вызова с помощью JavaScript методов ActionScript и выполнение этих вызовов

Получить файлы приложения для этого примера можно на странице www.adobe.com/go/learn_programmingAS3samples_flash_ru. Файлы приложения Introvert IM находятся в папке Samples/IntrovertIM_HTML. Приложение состоит из следующих файлов.

File

Описание

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

HTML-страница приложения для Flash (html-flash/IntrovertIMApp.html) или шаблон, который использовался для создания HTML-страницы приложения для Adobe Flex (html-template/index.template.html). Этот файл содержит все функции JavaScript, которые делают контейнер частью приложения.

Подготовка связи между ActionScript и обозревателем

Одно из наиболее частых применений внешнего API состоит в том, чтобы позволять приложениям ActionScript связываться с обозревателем. С помощью внешнего API методы ActionScript могут вызывать код, написанный на языке JavaScript, и наоборот. Из-за сложности обозревателей и их метода внутренней визуализации страниц не существует метода, гарантирующего, что документ SWF зарегистрирует обратные вызовы перед тем, как будет запущен первый код JavaScript на HTML-странице. По этой причине перед вызовом функций в SWF-документе из JavaScript ваш 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."); 
    } 
}

Первым делом код проверяет, доступен ли внешний API в текущем контейнере с помощью свойства ExternalInterface.available. При этом он приступает к установке подключения. Поскольку исключения, связанные с безопасностью, и другие ошибки могут происходить при попытке связи с внешним приложением, код упакован в блок 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. Поэтому метод isContainerReady() в ActionScript также возвращает true.

В конструкторе IMManager происходит одно из двух событий в зависимости от готовности контейнера. Если isContainerReady() возвращает true, код просто вызывает метод setupCallbacks(), который завершает процесс установки связи с JavaScript. С другой стороны, если isContainerReady() возвращает false, процесс по существу откладывается. Создается объект Timer, которому дается инструкция вызывать метод timerHandler() раз в 100 миллисекунд:

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 для принятия вызовов со стороны 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() использовался для вызова функции JavaScriptsetSWFIsReady(), которая уведомляет контейнер о том, что функции ActionScript были зарегистрированы.

Связь между ActionScript и обозревателем

Приложение Introvert IM демонстрирует целый ряд примеров вызова функций JavaScript на странице контейнера. В простейшем случае (пример из метода setupCallbacks()) функция setSWFIsReady() в JavaScript вызывается без передачи каких-либо параметров или получения в ответ значения:

ExternalInterface.call("setSWFIsReady");

В другом примере из метода isContainerReady() ActionScript вызывает функцию isReady() и получает в ответ логическое значение:

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

Передать параметры функциям JavaScript можно также с помощью внешнего API. Рассмотрим, к примеру, метод sendMessage() класса IMManager, который вызывается, когда пользователь отправляет новое сообщение своему «собеседнику»:

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

И снова здесь применяется метод ExternalInterface.call() для вызова назначенной функции JavaScript, уведомляя обозревателя о новом сообщении. Кроме того, само сообщение передается в качестве дополнительного параметра методу ExternalInterface.call() и соответственно передается в качестве параметра функции JavaScriptnewMessage().

Вызов кода ActionScript из JavaScript

Связь предполагает двусторонний обмен, и приложение Introvert IM не является исключением. Не только клиент системы мгновенного обмена сообщениями в проигрывателе Flash Player вызывает JavaScript для отправки сообщений. HTML-форма также вызывает код JavaScript для отправки сообщений и запроса информации из SWF-файла. Например, когда SWF-файл уведомляет контейнер о том, что он закончил устанавливать контакт и готов к связи, обозреватель первым делом вызывает метод getStatus() класса IMManager, чтобы узнать исходное состояние доступности пользователя из клиента системы мгновенного обмена сообщениями SWF. Это выполняется на веб-странице функции 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 = ...) фактически вызывает метод getStatus() в классе IMManager. В этой строке происходят три события.

  • Вызывается функция getSWF() для JavaScript, которая возвращает ссылку на объект JavaScript, представляющий SWF-файл. Параметр, переданный getSWF(), определяет, какой объект обозревателя возвращается в том случае, если на HTML-странице имеется более одного SWF-файла. Значение, переданное этому параметру, должно соответствовать атрибуту id тега object и атрибуту name тега embed, используемым для включения SWF-файла.

  • С помощью ссылки на SWF-файл метод getStatus() вызывается так, как если бы он был методом объекта SWF. В данном случае применяется имя «getStatus», потому что это то имя, под которым зарегистрирована функция ActionScript с помощью метода ExternalInterface.addCallback().

  • Метод getStatus() для ActionScript возвращает значение, и это значение присваивается переменной currentStatus, которая затем назначается в качестве содержимого (свойство value) текстового поля status.

Примечание. Если вы внимательно отслеживаете код, то, вероятно, заметили, что в исходном коде для функции updateStatus() строка кода, вызывающая функцию getSWF(), на самом деле записана следующим образом: var currentStatus = getSWF("${application}").getStatus(). Текст ${application} является местозаполнителем в шаблоне страницы HTML. Когда Adobe Flash Builder создает конкретную страницу HTML для приложения, этот местозаполнитель заменяется тем же текстом, который используется в качестве атрибута id тега object и атрибута name тега embed (в данном примере это текст IntrovertIMApp). Именно это значение и ожидается функцией getSWF().

Функция sendMessage() для JavaScript демонстрирует передачу параметра функции ActionScript. (sendMessage() — это функция, которая вызывается, когда пользователь нажимает кнопку «Отправить» на HTML-странице.)

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

Метод newMessage() для ActionScript ожидает один параметр. Поэтому переменная message для JavaScript передается в ActionScript. Она служит в качестве параметра при вызове метода newMessage() в коде JavaScript.

Определение типа обозревателя

Из-за различий в методе доступа обозревателей к содержимому важно всегда применять JavaScript, чтобы определить выбранный пользователем обозреватель и вызвать ролик в соответствии с синтаксисом для конкретного обозревателя с помощью объекта окна или документа, как показано в функции getSWF() для JavaScript в этом примере:

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

Если в вашем сценарии не определен тип обозревателя пользователя, пользователь может столкнуться с неожиданным поведением при воспроизведении SWF-файлов в контейнере HTML.