mxmlc: подключение внешней библиотеки (external library)
Запись от ps_spectre размещена 04.02.2011 в 23:18
Обновил(-а) ps_spectre 05.02.2011 в 14:14 (уточнение, что материал никакого отношения к Runtime Shared Libraries не имеет)
Обновил(-а) ps_spectre 05.02.2011 в 14:14 (уточнение, что материал никакого отношения к Runtime Shared Libraries не имеет)
Данная заметка является небольшим продолжением этой и этой заметки, но с точки зрения возможности подключения внешних библиотек.
update: материал никакого отношения к Runtime Shared Libraries не имеет. (это совсем другая тема, и вне флекса смысла нету использовать). runtime-shared-library-path и runtime-shared-libraries мы трогать не будем.
Итак, если мы компилируем проекты с помощью mxmlc компилятора, то у нас 3 варианта подключения библиотек (swc)
1. Включить полностью библиотеку в конечный swf файл.
2. Включить только те классы из библиотеки на которые есть ссылки в конечный swf файл.
3. Не включать вообще классы в конечный swf файл из библиотеки.
Сегодня рассмотрим 3й вариант более подробно.
Для чего нам вообще может понадобиться использовать библиотеку как внешнюю?
Предположим, мы разрабатываем приложение, которое использует десятки мегабайт ресурсов. (код, графика, звуки, музыка, ...)
Никаких проблем при публикации нашего конечного swf файла не возникло бы при следующих условиях:
- наличие сверх быстрого безлимитного интернета у каждого потенциального пользователя нашего приложения.
- сферическая идеальность нашей программы без единой ошибки. (зарелизили и забыли)
Но, в реальной жизни, все гораздо сложнее, наличие медленного интернет-соединения и постоянная правка багов и допиливание программы под постоянно изменяющиеся требования приводит к тем обстоятельствам, что нам надо постоянно вносить правки в наш код и заново пересобирать swf и заливать новую версию на сервер. (в случае онлайн игр -- постоянное введение новых фич)
В таком случае браузеру заново понадобится качать наш swf с сервера, т.к. в кэше будет старая версия.
А учитывая тот факт, что размер нашего приложения десятки мегабайт, пользователь, при низкой скорости интернета (а так же ограниченного кол-ва трафика), не захочет каждый раз ждать пока снова загрузится свежая версия с сервера. (а при ограниченном трафике еще и цена будет играть свою роль)
Естественный путь решения этой проблемы состоит в том, чтобы вынести неизменяемые классы/ресурсы из нашего swf контейнера, чтобы один раз клиент их загрузил и положил в кэш.
API флеш плеера позволяет во время исполнения (в run-time) подгружать ресурсы и код в нашу аппликуху. Для этого есть, как минимум, класс Loader, который позволяет грузить, как отдельно взятые ресурсы (.jpg, .png, .mp3, ...), так и swf контейнеры, которые можно загружать в наш домен приложения и как угодно использовать оттуда классы.
Итак, после того, как успешно загрузили swf loader'ом, начиная с этого момента, мы можем начать использовать классы из него, т.к. они уже есть у нас в памяти.
Как воспользоваться только-что загруженным классом? (например мы из game.swf загрузили assetLibrary.swf в котором есть класс SomeImagePng (extends Bitmap))
очевидно, что надо делать так: (в game.swf)
Game.as
package { import flash.display.Sprite; import flash.events.Event; import flash.display.Loader; import flash.net.URLRequest; import flash.system.ApplicationDomain; import flash.system.LoaderContext; import flash.display.Bitmap; public class Game extends Sprite { private var _loader:Loader; public function Game():void { //загружать надо в текущий домен var loaderContext:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain); _loader = new Loader(); _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteHandler); _loader.load(new URLRequest("assetLibrary.swf"), loaderContext); } private function loaderCompleteHandler(event:Event):void { init(); } private function init(e:Event = null):void { var b:Bitmap = new SomeImagePng(); addChild(b); } } }
Но... конечно же при попытки скомпилировать этот код (mxmlc Game.as) мы получаем ошибку от компилятора, который утверждает что класс SomeImagePng у нас не определен.
Как же так, мы то ведь точно знаем, что после загрузки assetLibrary.swf он у нас точно есть, ведь мы его сами туда (в assetLibrary) запихнули!
Что же делать?
Например мы можем получить ссылку на класс (во флеше классы, как и функции, относятся к типу first-class object), который есть в assetLibrary.swf следующим образом:
добавив одну строчку
var SomeImagePng:Class = ApplicationDomain.currentDomain.getDefinition("SomeImagePng") as Class; var b:Bitmap = new SomeImagePng(); addChild(b);
Но мы не этого хотим. Наша цель сделать так, чтобы работал первый вариант
и компилятор mxmlc знал однозначно во время компиляции, есть ли у нас этот класс или нет.
Для того чтобы понять как это сделать, рассмотрим следующий простой пример:
у нас есть класс
Main.as
package { import flash.display.Sprite; public class Main extends Sprite { public function Main() { } } }
<flex-config> <compiler> <as3>true</as3> <debug>false</debug> <es>false</es> <optimize>true</optimize> <strict>true</strict> </compiler> <default-background-color>0x99FFFF</default-background-color> <default-frame-rate>30</default-frame-rate> <default-size> <width>800</width> <height>600</height> </default-size> <swf-version>10</swf-version> <target-player>10.1.0</target-player> <use-network>true</use-network> </flex-config>
Код:
mxmlc Main.as -load-config conf.xml
сol: 28 Error: The definition of base class Sprite was not found.
Как же так? Ведь мы то знаем, что в рантайме флешплеер любезно предоставляет эти классы в нашем домене, перед тем как наш main.swf загрузить.
Но вот во время компиляции флекс-компилер отказывается компилировать сие, т.к. не знает о наличии Sprite и вообще, о flashplayer ничего не знает.
Ну это и понятно, ведь флеш-плееров, реализаций, могут быть сотни, и все разные.
Поэтому нам надо сделать так, чтобы компилятор понял, каким набором классов мы обладаем.
Для этого, собственно, библиотеки с компилированным кодом и надо. Например, если мы пишем под флеш плеер 10.2, то нам понадобится swc библиотека от этого плеера.
Взять ее можно из flexsdk/frameworks/libs/player/10.2/playerglobal.swc. (в 4.5 hero 10.2 идет)
Чтобы нагляднее было, я ее перенес в то место, где наши файлы Main.as и conf.xml (в подкаталог player, и переименовал player.swc)
Теперь возвращаемся к сабжу.
И так, у нас есть 3 варианта коннекта библы в наш файл. Первых два, нам не подходят, т.к. нет смысла включать 200+ килобайт в наш файл, потому что flashplayer plugin или standalone flashplayer и так имеет эти классы в наличии.
Поэтому посмотрим 3й вариант, подключим ее как внешнюю библиотеку. Это даст возможность компилятору в compile-time отслеживать правильность и наличие классов которые мы используем.
Чтобы подключить player.swc из нашего подкаталого player добавим следующие строчки в конфиг файл:
Код:
<external-library-path> <path-element>./player/player.swc</path-element> </external-library-path>
Код:
<flex-config> <compiler> <as3>true</as3> <debug>false</debug> <es>false</es> <optimize>true</optimize> <strict>true</strict> <external-library-path> <path-element>./player/player.swc</path-element> </external-library-path> </compiler> <default-background-color>0x99FFFF</default-background-color> <default-frame-rate>30</default-frame-rate> <default-size> <width>800</width> <height>600</height> </default-size> <swf-version>10</swf-version> <target-player>10.1.0</target-player> <use-network>true</use-network> </flex-config>
Код:
mxmlc Main.as -load-config conf.xml
(внимательный читатель заметил, что таким способом -load-config conf.xml мы перетерли конфиг который флекс использует по-умолчанию, в конфиге флекса всегда стоит линковка внешней библиотеки playerglobal.swc)
Кстате, при таком минимальном конфиге, размер получившейся флехи с помощью флекс-компилира из флекс-сдк 4.5
получился 284 байта, и в непожатом виде 363
При таком маленьком размере, имеет смысл в будущем поизучать базовую структуру swf контейнера, т.к. в результате ничего лишнего нету.
Теперь вернемся к нашему прошлому примеру.
У нас есть в наличии assetLibrary.swc (в подкаталог /libs запихнем) в котором лежит SomeImagePng
в .swc архиве два файла library.swf и catalog.xml, сделаем копию оттуда файла library.swf переименуем в assetLibrary.swf (на котором выше пробовали делать)
так же есть класс Game.as
еще раз взглянем как там картинка создается
При наших прошлых попытках скомпилировать, ничего не получалось, т.к. флекс не знал где искать этот класс.
И нам приходилось использовать getDefinition.
Сейчас же мы научились указывать флексу внешнии библиотеки, из которых он знает какие классы мы используем.
Раз библиотека помечена как внешняя, то нам надо руками самим в рантайме грузить любой swf где этот класс (из swc) будет в наличии. (самое простое создать swc, и оттуда вынуть .swf)
Иначе эксепшен будет, что тип не найден.
Добавим еще один класс чтобы более реалистично было, это будет Preloader.as из прошло примера.
И так что у нас есть: Preloader.as, Game.as, assetLibrary.swf, /libs/assetLibrary.swc
теперь осталось это добро скомпилировать:
Код:
mxmlc Preloader.as -frame 1 Game -external-library-path+="./libs" -debug -output game.swf
при паблише выкладываем два файла на сервер, и теперь когда меням код, библиотеку можно не трогать. И теперь мы можем хоть каждую ночь делать новый билд swf и выкладывать на сервер, основные ресурсы (коих может быть на сотни мегабайт) трогать при исправлении кода нам не нужно. (конечно, если нам вдруг надо поменять картинку из сотни в свиф файле, тогда хуже...(лучше наверно пачками разные картинки в разные swf ложить, тут кому какой подход нравится )
И при всем этом, мы получаем все преимущества swc библиотек.
Для наглядности я выкладываю архив с этими файлами. (очень базовый код) (в примере используется очень плохая картинка, первое png под руку попавшее, скрин одного окна флешбилдера)
Если есть FlashIDE то можно открыть во встроенном флеш плеере, выбрать маленькую скорость, и запустить simulate download и наглядно посмотреть как прелоадер вначале прогресс себя отображает, а потом уже прогресс загрузки .swf. (это сделать можно так: открываем FlashIDE -> file -> open -> game.swf выбираем, дальше откроется флешплеер встроенный во флешИДЕ, в нем выбираем View -> download settings -> выставляем мин. скорость, дальше выбираем View -> Simulate Download (control/command + enter))
Примерно как это будет на нашем примере выглядеть во вложении.
Итоги: была рассмотрена базова техника работы со внешними библиотеками. Для более детального углубления смотреть Хелп и читать Мука. (в целом)
Внимание: еще раз хочу сделать ударение на том, что при таком подходе, загрузка классов в runtime ложится только на наши плечи, не на флеш плеер, не на браузер (в случае флеш плеер плагина).
Поэтому, прежде чем использовать классы, нам их надо загрузить или просто получим exception, что флеш плеер не находит класс.
Всего комментариев 20
Комментарии
05.02.2011 00:57 | |
Спасибо за прекрасные статьи! Надеюсь, что вы и дальше будете радовать таким подробным материалом
|
05.02.2011 01:37 | |
Очень познавательно. какие то кусочки мозаики я и раньше имел, но всё сложить в кучу - очень хорошо.
только Цитата:
конечно, если нам вдруг надо поменять картинку из сотни в свиф файле, тогда хуже...
Я извращался изменением каждый раз при релизе html параметра base и заливкой туда новых версий external библиотек, что на самом деле убивает напрочь полезный эффект. Если в avm1 - это было оправданно, ибо по другому нельзя было грузить ресурсы "внутрь" других мувиков, то в avm2 - это анахронизм, который можно использовать только для flex-либ. Об этом стоит добавить. Правило - используйте runtime external library, когда вы на 100500% уверены, что либа не изменится. Могу быть не прав - поправьте меня. |
|
Обновил(-а) Котяра 05.02.2011 в 02:35
|
05.02.2011 12:41 | |
iNils, спасибо за хорошие слова, в будущем постараюсь и дальше писать в блог.
Котяра, так в рантайме грузим мы обычный swf, в котором будут наши классы, который по-сути, никакого отношения к swc может и не иметь. По-большому счету, компилятору все равно, что находится в swc, который мы указали как внешний. Он оттуда смотрит файл catalog.xml и те классы которые там указаны, не обращает на них во время компиляции, и ему (компилятору) все равно, будут они у нас в рантайме или нет. Его дело скомпилировать только. Такого же эффекта можно было достичь, если был бы ключ, аля -ignore-undefined-types Поэтому проблемы с загрузкой такие же, как просто грузить отдельный swf, а оттуда getDefinition() дергать классы. Только вместо getDefinition() мы напрямую к ним обращаемся. В примере, что я приложил к статье, во время запуска флешки появляются два прогресс бара, первым мы отслеживаем загрузку самого нашего swf (типа наше ядро) , вторым загрузку подгружаемого swf. (типа ресурсы) [IMG]http://img815.**************/img815/2787/loader.png[/IMG] (пример очень мало весит, поэтому чтобы это посмотреть, надо сэмулировать во flashIDE с самой маленькой скоростью) p.s. я могу без проблем ошибаться и делать не правильные выводы, т.к. всегда в продакшене все гораздо сложнее, и косяки могут появляться там, где их не ждали. Но базовая техника работает. Или же я не так Вас понял, поделитесь своим методом пожалуйста, возможно я что-то упускаю из виду. update: материал никакого отношения к Runtime Shared Libraries не имеет. (это совсем другая тема, и вне флекса смысла нету использовать) |
|
Обновил(-а) ps_spectre 05.02.2011 в 12:53
|
05.02.2011 13:07 | |
Извините, это я затупил. Почему-то для меня external library прочно заассоциировались с Runtime Shared Libraries.
|
05.02.2011 13:13 | |
Цитата:
материал никакого отношения к Runtime Shared Libraries не имеет
http://livedocs.adobe.com/flex/3/htm...nt=rsl_01.html |
05.02.2011 13:31 | |
i.o., простите, я не согласен. Я описал опцию компилятора
<external-library-path> и к RSL, в данном контексте, отношения никакого не имеет. Т.к. загрузка классическая, руками из кода: var loaderContext:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain); _loader = new Loader(); _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteHandler); _loader.load(new URLRequest("assetLibrary.swf"), loaderContext); Чтобы сделать RSL надо другой подход. -static-link-runtime-shared-libraries -runtime-shared-library-path и т.д. а так же там еще и *.swz могут выплыть и т.д. Вообщем, RSL это другая тема, и вне flex я смысла мало вижу использовать. Ибо изначально это надо было, чтобы флекс приложения не весили мин 500 килобайт при одном hello world. во flash builder 4 это примерно так выглядит: (мы flex можем линковать как RSL) [IMG]http://img823.**************/img823/6929/flashbuilder.th.png[/IMG] |
|
Обновил(-а) ps_spectre 05.02.2011 в 13:42
|
05.02.2011 13:45 | |
Еще раз спасибо, дошел до все этой крути с помощью Вашего прошлого поста.
Я думаю, еще стоит добавить одну коротенькую строчку про FleshDevelop - как там это настраивается... |
05.02.2011 14:08 | |
Обновил пост, добавил картинки, как выбрать эту опцию во FlashBuilder и FlashDevelop.
Всем спасибо за обратную связь, критика, указание неточностей и просто советы — приветствуются. |
07.02.2011 19:43 | |
Цитата:
а так же там еще и *.swz могут выплыть и т.д.
|
07.02.2011 19:49 | |
Да, речь шла об АппДомене.
|
07.02.2011 23:11 | |
Да, с external-library разобрался, спасибо. Но теперь стал интересен RSL как чисто теоретическая штука =)
|
07.02.2011 23:38 | |
Для ActionScript проектов юзайте способ ps_spectre, ну или так http://riapriority.com/blogs/slon-vs...runtime_shared
Все таки это больше фишка для флекса, особенно учитывая, что swz кэшируются самим плеером, а не браузером. |
20.03.2011 21:43 | |
Последние записи от ps_spectre
- Используем фичи flash player 10.2 или 11.0 во FlashIDE CS5 (08.03.2011)
- mxmlc: подключение внешней библиотеки (external library) (04.02.2011)
- Создание многокадровой флешки с помощью mxmlc (03.02.2011)
- Создание библиотеки ресурсов (swc) с помощью compc (02.02.2011)