Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > ps_spectre

Рейтинг: 5.00. Голосов: 4.

mxmlc: подключение внешней библиотеки (external library)

Запись от ps_spectre размещена 04.02.2011 в 23:18
Обновил(-а) 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)
Код AS3:
var b:Bitmap = new SomeImagePng();
addChild(b)
Game.as
Код AS3:
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);
		}
	}
}
Работает в рантайме прекрасно, мы загрузили в наш домен assetLibrary, у нас появилась возможность использовать оттуда класс, чем мы и воспользовались.

Но... конечно же при попытки скомпилировать этот код (mxmlc Game.as) мы получаем ошибку от компилятора, который утверждает что класс SomeImagePng у нас не определен.
Как же так, мы то ведь точно знаем, что после загрузки assetLibrary.swf он у нас точно есть, ведь мы его сами туда (в assetLibrary) запихнули!

Что же делать?

Например мы можем получить ссылку на класс (во флеше классы, как и функции, относятся к типу first-class object), который есть в assetLibrary.swf следующим образом:

добавив одну строчку

Код AS3:
var SomeImagePng:Class = ApplicationDomain.currentDomain.getDefinition("SomeImagePng") as Class;
var b:Bitmap = new SomeImagePng();
addChild(b);
теперь при компиляции mxmlc не будет ругаться.

Но мы не этого хотим. Наша цель сделать так, чтобы работал первый вариант
Код AS3:
var b:Bitmap = new SomeImagePng();
addChild(b);
и компилятор mxmlc знал однозначно во время компиляции, есть ли у нас этот класс или нет.

Для того чтобы понять как это сделать, рассмотрим следующий простой пример:

у нас есть класс

Main.as
Код AS3:
package
{
	import flash.display.Sprite;
	public class Main extends Sprite
	{
		public function Main() { }					
	}
}
в этой же директории создадим файл

Код AS3:
conf.xml
Код AS1/AS2:
<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>
conf.xml
Код:
<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
Зашибись, все пашет и ничего дополнительного из player.swc не попадает в наш swf. (так как в рантайме доступ к ним у нас и так будет)

(внимательный читатель заметил, что таким способом -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
еще раз взглянем как там картинка создается
Код AS3:
 
			var b:Bitmap = new SomeImagePng();
			addChild(b);
При наших прошлых попытках скомпилировать, ничего не получалось, т.к. флекс не знал где искать этот класс.
И нам приходилось использовать 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
в итоге получаем game.swf ~3килобайта и assetLibrary.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, что флеш плеер не находит класс.
Изображения
Тип файла: png loader.png (2.4 Кб, 1789 просмотров)
Тип файла: png FlashBuilderExternal_small.png (179.2 Кб, 1803 просмотров)
Тип файла: png flash-develo-external.png (32.8 Кб, 1937 просмотров)
Вложения
Тип файла: zip Preloader_and_externalLib.zip (189.0 Кб, 253 просмотров)
Размещено в Mxmlc and compc
Комментарии 20 Отправить другу ссылку на эту запись
Всего комментариев 20

Комментарии

Старый 05.02.2011 00:57 iNils вне форума
iNils
 
Аватар для iNils
Спасибо за прекрасные статьи! Надеюсь, что вы и дальше будете радовать таким подробным материалом
Старый 05.02.2011 01:37 Котяра вне форума
Котяра
 
Аватар для Котяра
Очень познавательно. какие то кусочки мозаики я и раньше имел, но всё сложить в кучу - очень хорошо.
только
Цитата:
конечно, если нам вдруг надо поменять картинку из сотни в свиф файле, тогда хуже...
Я вовсю использовал external ещё на as2, но этот метод имеет много минусов. трудно практически невозможно отследить прогресс загрузки и контроллировать кэш тоже.
Я извращался изменением каждый раз при релизе html параметра base и заливкой туда новых версий external библиотек, что на самом деле убивает напрочь полезный эффект. Если в avm1 - это было оправданно, ибо по другому нельзя было грузить ресурсы "внутрь" других мувиков, то в avm2 - это анахронизм, который можно использовать только для flex-либ.
Об этом стоит добавить.
Правило - используйте runtime external library, когда вы на 100500% уверены, что либа не изменится.
Могу быть не прав - поправьте меня.
Обновил(-а) Котяра 05.02.2011 в 02:35
Старый 05.02.2011 12:41 ps_spectre вне форума
ps_spectre
 
Аватар для ps_spectre
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 i.o. вне форума
i.o.
 
Аватар для i.o.
Цитата:
материал никакого отношения к Runtime Shared Libraries не имеет
Как это не имеет? В статье вы описали ее частный случай.
http://livedocs.adobe.com/flex/3/htm...nt=rsl_01.html
Старый 05.02.2011 13:31 ps_spectre вне форума
ps_spectre
 
Аватар для ps_spectre
i.o., простите, я не согласен. Я описал опцию компилятора
<external-library-path> и к RSL, в данном контексте, отношения никакого не имеет.

Т.к. загрузка классическая, руками из кода:
Код AS3:
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 надо для того, когда у нас несколько приложений используют одну и ту же библиотеку. Например flex-framework. И здесь у нас уже нету механизмов отслеживания как браузер с флеш плеером использует RSL.

Чтобы сделать 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 zuxul вне форума
zuxul
Еще раз спасибо, дошел до все этой крути с помощью Вашего прошлого поста.
Я думаю, еще стоит добавить одну коротенькую строчку про FleshDevelop - как там это настраивается...
Старый 05.02.2011 14:08 ps_spectre вне форума
ps_spectre
 
Аватар для ps_spectre
Обновил пост, добавил картинки, как выбрать эту опцию во FlashBuilder и FlashDevelop.

Всем спасибо за обратную связь, критика, указание неточностей и просто советы — приветствуются.
Старый 06.02.2011 20:49 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Очень здорово, спасибо!
У меня появился вопрос.
Создавая вот так явно new MyClass() — мы используем текущий аппдомен. Означает ли это, что используя внешние библиотеки невозможно будет добиться разбиения по разным доменам?

И вопрос к нашим мэтрам. Что именно используете Вы? Ранее, я брал из аппдомена по getDefinition и приводил к интерфейсу, тем самым добавляя статик-типизации. Но данный способ мне кажется куда вкуснее из за ненужды плодить интерфейсы. Какие подводные камни?
Старый 06.02.2011 21:10 mayakwd вне форума
mayakwd
 
Аватар для mayakwd
Радует доступность материала. Но нового для себя я ничего не открыл.

Psycho Tiger, я хоть и не мэтр, но отвечу. Я использую именно такой подход, большую часть кода, ассетов выношу в неизменные либы, которые грузятся, кэшируются, и при изменениях в основном коде не нуждаются в повторной загрузке, и да - отпадает необходимость в интерфейсах.
Старый 07.02.2011 17:02 ps_spectre вне форума
ps_spectre
 
Аватар для ps_spectre
Psycho Tiger,
Цитата:
Означает ли это, что используя внешние библиотеки невозможно будет добиться разбиения по разным доменам?
Если я правильно понял вопрос, то ответ: нет, не означат.

Предположим у нас 2 разных домена:
Код:
www.site.com и local.site.com
с
Код:
www.site.com/game.swf
мы можем загружать
Код:
local.site.com/assetLibrary.swf
но при условии, что у нас есть
Код:
 local.site.com/crossdomain.xml
в котором мы явно разрешаем
Код:
<?xml version="1.0"?>		
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
	<allow-access-from domain="www.site.com" />
</cross-domain-policy>
иначе ошибка будет:
Код:
Error #2044: Unhandled SecurityErrorEvent:. text=Error #2048: Security sandbox violation: http://www.site.com/game.swf cannot load data from http://local.site.com/assetLibrary.swf.
Старый 07.02.2011 19:43 alatar вне форума
alatar
 
Аватар для alatar
Цитата:
а так же там еще и *.swz могут выплыть и т.д.
Хреново, что swz могут выплыть только от адоба. С нами веслами они делиться не хотят
Старый 07.02.2011 19:45 gloomyBrain вне форума
gloomyBrain
 
Аватар для gloomyBrain
Я думаю, имелось ввиду ApplicationDomain - просто при загрузке в разные аппДомены есть возможность чистить память от ненужных ассетов. При описанном выше подходе такой возможности не наблюдается
Старый 07.02.2011 19:49 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Да, речь шла об АппДомене.
Старый 07.02.2011 21:01 ps_spectre вне форума
ps_spectre
 
Аватар для ps_spectre
gloomyBrain, Psycho Tiger, а-а-а, сорри, не так сразу понял.
Правда, к сожалению, в другой домен грузить при таком подходе не получится. (да, память может стать определенной проблемой )
но все равно у нас всегда есть возможность грузить в отдельный аппдомен и использовать
ApplicationDomain.get/hasDefinition, тут надо смотреть по проекту, как удобней/лучше делать.

alatar, для меня это вообще еще не совсем прозрачная тема с подпиской rsl

i.o., все же Вы правы, в принципе это можно спокойно называть частным случаем.


2all вопросы связанные с security domains/application domains и смежными темами у флеша являются интересной и обширной темой. Я уверен, что могу где-то лажать в своих суждениях и/или ошибаться, поэтому все советы/критика/исправления и т.д. очень сильно приветствуются. ))
Старый 07.02.2011 22:33 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Кстати, раз уж тут пьянка пошла...
Я, каюсь, никогда не использовал RSL. Потому что умные дяди говорили, что оное ненужно, ведь есть getDefinition. И до сих пор говорят что RSL не лучшее решение. И как-то оное прошло мимо меня боком...
Можете подсказать чего-нибудь? Я не имею ввиду писать статью (хотя, конечно, это было бы прекрасно: Вас очень приятно читать), а так, где об этом можно разузнать. (про хэлп я знаю, но можно и туда отправить))
Старый 07.02.2011 22:48 ps_spectre вне форума
ps_spectre
 
Аватар для ps_spectre
Psycho Tiger, я тоже считаю что RSL не нужно для as3... может для какого-нить гигантского энтерпрайз на флексе и можно смотреть в сторону RSL

только опять таки, external-library это просто фишка сказать компилятору, что во время компиляции не нужно обращать внимание на отсутствие определения классов. (он смотрит catalog.xml из *.swc)
(этого, в принципе, можно достичь компилируя не в strict режиме, ))
Старый 07.02.2011 23:11 Psycho Tiger вне форума
Psycho Tiger
 
Аватар для Psycho Tiger
Да, с external-library разобрался, спасибо. Но теперь стал интересен RSL как чисто теоретическая штука =)
Старый 07.02.2011 23:38 alatar вне форума
alatar
 
Аватар для alatar
Для ActionScript проектов юзайте способ ps_spectre, ну или так http://riapriority.com/blogs/slon-vs...runtime_shared
Все таки это больше фишка для флекса, особенно учитывая, что swz кэшируются самим плеером, а не браузером.
Старый 20.03.2011 21:43 fljot вне форума
fljot
http://jansensan.net/loading-an-swc-...different-ides

в догонку, вдруг кому пригодится
 

 


Часовой пояс GMT +4, время: 23:33.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.