| « Debug mode для FDT 3 | Flash Platform: User-группы » |
RSL в ActionScript Project: разделяемый между приложениями код
Ситуация: в проекте несколько приложений, которые используют общий, довольно независимый код.
Например: серия игр, использующих 2D-движок APE или несколько видов сайта (главная, почта, чат), которые используют общие классы для работы с сервером статистики.
Проблема: каждая .swf содержит в себе этот код.
- Это увеличивает их размер
- При модификации этого кода нужно перекомпилировать все зависящие от него приложения (например, при изменении способа обращения к серверу)
Решение: вынести этот код в отдельный подгружаемый модуль. Во Flex для этого есть Runtime Shared Library (RSL), однако в ActionScript Project вам придется написать часть этой функциональности самому.

Это фишка именно Flex Framework. RSL-библиотеки подгружаются на этапе прелоадера: в классе mx.preloaders.Preloader есть методы, отвечающие за их загрузку. Какие RSL использовать и как их подключать (включить в код, подгружать в runtime) можно указать в меню Properties проекта. Flex также проверяет RLS-библиотеки на совместимость с текущей версией главного файла - для этого используются xml-манифесты - описания содержимого библиотеки. У нас этого нет, но мы с Вами можем реализовать, например, шифрование библиотек (если надо).
В ActionScript Project это нужно делать самому. Это нужно сделать прозрачно для приложения, т.е. все удобства работы с классами должны сохраниться:
- Подсказки в редакторе
- Использование ресурсов библиотеки без каких-либо проверок на загруженность (напр. классов или графики)
- Ничего не проверять и не загружать руками в каждом проекте отдельно
Создадим ActionScript Project, результирующий .swf которого будет содержать библиотеку ape.swc (воспользуемся опцией компилятора -include-libraries)
В наш основной проект также добавим эту библиотеку как .swc, однако выберем для нее Link Type: External (в ActionScript Build Path -> Library Path -> ape.swc -> Link Type), таким образом компилятор будет учитывать эти классы при компиляции, но не включит в .swf .
Выходной .swf проекта-обертки будем подгружать в прелоадере других основных проектов, до начала выполнения основных классов. Для каждого приложения будет собственный прелоадер, наследующий от класса DefaultPreloader (в нем - вся логика подгрузки и запуска). В конкретном прелоадере MyAppPreloader extends DefaultPreloader нужно будет указать массив адресов библиотек и имя главного файла приложения.
После этого в конструкторе главного класса приложения MyApp.as можно использовать все классы библиотеки, при этом они не включены в .swf проекта.
Пример на основе APE: на сайте можно скачать бинарный код ape.swc, но мы не можем загружать .swc файлы - только .swf .
Структура примера:
- PortalProto.swf - 3 kb
- UserProfile.swf - 5 kb
- lib/APE.swf - 20 kb
1. Создадим проект-обертку для ape.swc:
- Создаем обычный ActionScript Project, назовем APE
- Теперь нужно, чтобы система включила lib/ape.swc в APE.swf: добавим в ActionScript Compiler -> Additional compiler options -include-libraries “../lib/ape.swc” (путь к ape.swс относительно главного класса приложения APE.as)
Теперь у нас есть APE.swf, который содержит в себе все необходимые классы.
2. Создадим ActionScript проект SomeGame, cкопируем файл APE.swf в его папку bin/lib/. Создадим прелоадер, для этого напишем:
- package {
- // Импорты убраны для экономии места,
- // в коде их нужно дописать
- [SWF(width="800", height="600", backgroundColor="0xFFFFFF")]
- [Frame(factoryClass="SomeGamePreloader")]
- public class PortalProto extends Sprite
- {
- public function PortalProto()
- {
- trace("PortalProto::PortalProto");
- addEventListener(
- Event.ADDED_TO_STAGE,
- addedToStageHandler);
- }
- protected function init():void
- {
- stage.frameRate = 60;
- addEventListener(Event.ENTER_FRAME, run);
- APEngine.init(1/4);
- APEngine.container = this;
- APEngine.addMasslessForce(new Vector(0,2));
- // Добавим элементы в движок
- // ...
- }
- // events handler
- protected function addedToStageHandler
- (event:Event):void
- {
- init();
- }
- protected function run(event:Event):void {
- APEngine.step();
- APEngine.paint();
- }
- }
- }
Обратите внимание на строчку [Frame(factoryClass="SomeGamePreloader")] - таким образом мы создаем структуру из двух кадров - сначала грузится класс SomeGamePreloader, который показывает процесс загрузки приложения и его библиотек. Код для SomeGamePreloader:
- package
- {
- public class SomeGamePreloader extends DefaultPreloader
- {
- public function SomeGamePreloader()
- {
- // Вызовем конструктор DefaultPreloader с
- // параметрами - массив адресов библиотек
- // и именем главного класса проекта
- super(["lib/APE.swf"], "SomeGame");
- }
- }
- }
Класс DefaultPreloader (код) обеспечивает всю логику загрузки библиотек, создания экземпляра главного класса приложения и добавления его на сцену.
Трекбек адрес этой записи
URL трекбека (щелкните правой кнопкой мыши и скопируйте ссылку)
12 комментариев
->
APEngine.init(1/4);
APEngine.container = this;
APEngine.addMasslessForce(new Vector(0,2));
Спасибо за статью.Интересен следующий момент. Для загрузки ape.swc, swc заворачивается в swf только из соображений возможности загрузки. Но ведь swc - уже swf запакованный в zip-архив. Почему бы вместо обёртки не распаковать, и не поиспользовать непосредственно swf из архива?
Спасибо большое за ответ.
Степа, совершенно верно. Просто раньше я этого не знал
Появился еще один вопрос. На счёт собственно метатега [Frame]. Очень интересна последовательность инстанциирования самого документ-класса (PortalProto в примере), и класса загрузчика (SomeGamePreloader). В первом кадре документ-классом является загрузчик, а только для второго кадра он сменяется на непосредственно PortalProto? Звучит странно для меня. ПРивык думать, что документ класс - это нечто, что один раз приклеили так, что не отклеить.Нашёл ссылочку по этой теме: http://nondocs.blogspot.com/2007/04/metadataframe_22.html , там внутри есть еще другая ссылка по теме. Но оба поста говорят в общих словах, и не объясняют подробно. К слову, блог nondocs интересный очень.
Буду очень рад, если вы поделитесь тем, что у вас есть по этой теме. Тема цепляет больно.
Спасибо большое.
Насколько я понял, понятие документ-класса не отображается напрямую в байт-код. Это понятие есть в языке программирования, но его нет в байт-коде. Также, например, понятие switch-блока есть в языке, а в байт-коде оно превращается в группу операторов.Зато в байт-коде есть понятие класса для фрейма. Все они совершенно равноправны за исключением класса для первого фрейма: для него при входе в конструктор класса экземпляр уже добавлен на сцену.
Снова возвращаясь к вашей статье. Есть готовое приложение. Теперь нужно модифицировать его так, оно во время загрузки самого себя показывало прогресс. Решение очевидно - просто использование метатега [Frame] так, как описано в вашей статье. Но что если мне хочется в фактори-классе поиспользовать какие-то внешние ресурсы, например отобразить логотип над прогрессбаром. Добавление к проекту swc файла с графикой приведёт к добавлению всей графики как раз в кадр с основным приложением (то есть графика доступна только из основного класса), а не в кадр, где работает фактори-класс. Как быть? Грузить графику для фактори класса ручками, или есть какое-то хитрое решение?
> Добавление к проекту swc файла с графикой приведёт к добавлению всей графики как раз в кадр с основным приложениемДобавление SWC приведет только к тому, что Вы сможете использовать классы из этого SWC. Если вы используете классы из этого SWC в прелоадере - эти классы будут включены в кадр с прелоадером т.е. все будет хорошо.
Но вообще есть мнение что логотипы к прелоадерам лучше делать внешними и подгружать в процессе показа прелоадера.
Максим, вы никогда при таком хитром методе загрузки не сталкивались с проблемами импорта звуковых файлов из SWC? Использую DefaultPreloader, SWC со звуками добавлен к проекту с Linkage Type: Merged into code, звуки не используются в DefaultPreloder, первое использование классов ассоциированных со звуками начинается только в основном классе приложения. При это вылетает ошибка:TypeError: Error #2023: Class MenuMusic$ must inherit from Sprite to link to the root.
Насколько я понимаю, такое бывает, когда документ-классом назначают не наследника спрайта. Но у меня и в мыслях такого не было. Просто инстанциирую звук в недрах приложения, и играю его себе дедушкиными методами sound.play(), никаких документ-классов не трогая.
Может вы сталкивались. Был бы рад, если бы поделились опытом. Спасибо!
Перенёс все звуки из библиотеки FLA файла в новый пустой FLA файл. Пересобрал библиотеку звуков. Ошибка пропала. Видимо перемудрил с библиотекой. Извините...
Как показала практика, этот рантайм эксепшн не вылетает, если не трогать компрессию аудио символов в библиотеке. Изменение компрессии в окне Паблиш ошибку не влечёт.
Да, во Flash IDE еще много неизвестных нам багов





