Рубрики: Development, ActionScript 2.0, ActionScript 3.0, Flash, Flex, Stories
6-ая RAFPUG
Автор фотографий - Алексей «Vooparker» Аникутин
На 6-ой встрече нашей RAFPUG Constantiner предложил сделать 30-минутный доклад о проблемах и решениях flash-клиента на портале top4top.ru. Я предложил своим коллегам Филиппу «FSB» Бондареву и Игорю «peace» Андрееву сделать наш доклад вместе (по сути переложил на них всю работу
), и так уж получилось что они вместо 25 минут рассказывали 2 часа.
Начал наш архитектор, Игорь. Одна из первых фраз была типа «то ли руки не оттуда ростут, то ли условия были таковы», на что Артемий предложил не впадать в уныние
Сотрудники Flexis соорудили доступ в интернет, и Игорь перешел от слайдов к показу живого сайта через Internet Explorer.
То ли за полгода мы так и не соорудили нормальное определение версий, то-ли и правда в IE был старый плеер, но все увидели предложение обновить плеер вместо top4top
Фидбек от Flexis? Какой там плеер в IE был? В Опере все сразу заработало. Следующие 20-30 минут Игорь ходил по top4top.ru в браузере и оказалось, что функционала-то немало.

Затем он перешел к более системным вещам и решениям, скоро тут появиться его презентация.
Настала очередь Филиппа. Он рассказывал про User Account, показывал запретные картинки, создавал и уничтожал планеты во вселенной.

Особенно запомнился момент, когда Филипп стал убирать красные глаза с помощью инструмента редактирования фото в User Account’е «Человеку-Жопе», символу IT-отдела портала:

Презентация Филиппа:
После короткого перерыва я за 5 минут протараторил свою презентацию:
Выводы (Филипп и Игорь со мной не согласны, их выводы я надеюсь тоже добавить в пост):
- Использование двух кривых версий (flash и html) одного контента - это победа технологий над здравым смыслом.
- На данный момент мне ясно, что лучшим является html решение с вставками flash там, где это абсолютно необходимо и с возможностью получить хоть что-то если FP недостаточно высокой версии
- Требование установить последний релиз плеера отпугнет от Вас бОльшую часть аудитории (если она состоит не из флешеров)
- Если не страшен предыдущий пункт и решено сделать портал на FP, то необходимо использовать Flex Framework
И, наконец, главный вывод:
- Необходимо предоставлять сервис максимального качества при текущей конфигурации машины пользователя, без необходимости установки обновлений чего-либо. Если он захочет (и у него есть такая возможность), то установит все, что попросите.

Я.
В целом получилось позитивно и, надеюсь, не очень занудно. Спасибо вам за вопросы и за то, что пришли.

Отвечаем на разные вопросы.
Настала очередь Артемия. Его доклад о нововведениях во Flash Player 10 Beta состоял из демонстрации множества примеров и активных обсуждений в зале. Мне больше всего понравилась генерация звука - вспомнился MS DOS, и сохранение файлов без участия сервера. Большое спасибо ему за доклад!

top4top - перемены
Несколько дней назад на балконе нашего офиса на базе недостроенного особняка-театра произошло историческое собрание всех. Было сказано много всего, про себя могу сказать что скорее всего в течении месяца-двух сменю место работы.








Профилирование ActionScript 3.0 проекта
Если Вы не раз профилировали проект, то что-то интересное, возможно, найдете ближе к концу поста.
Вступление
На данный момент единственный инструмент для профилирования это Flex Builder Profiler (хотя необходимый профайлеру API открыт - делай кто хочешь).
В процессе разработки top4top.ru не раз возникали проблемы с утечками памяти, иногда для их устранения приходилось даже вызывать разработчиков в офис по ночам. Иногда память не освобождалась вообще - росла с шагом 50-200 mb от числа переходов по страницам:

Это очень заметно и чинятся, как правило, за несколько часов. Сложнее сказать, есть ли утечка памяти, и где она по такому графику:

Тут уже мало что понятно. Поэтому первый вопрос - как определить, есть ли в приложении утечка памяти.
Определение факта утечки памяти - общие моменты
Тут нам очень поможет функция взятия разности между двумя dump-ами памяти приложения:

Результат - таблица Loitering Objects View, цитата из хелпа:
The Loitering Objects view shows you the differences between two memory snapshots of the application that you are profiling. The differences that this view shows are the number of instances of objects in memory and the amount of memory that those objects use. This is useful in identifying memory leaks. The time between two memory snapshots is known as the snapshot interval.
Для нас в ней самое интересное это имена классов и количество их оставшихся экземпляров.

На схеме выше приведен первый приходящий в голову вариант по поиску memory-leak-ов на Главной Странице. Однако он не работает - в список “бездомных” объектов попадают не только лики с Главной страницы, но и все объекты, создаваемые в Профайле 1.
Определение факта утечки памяти - стратегии
Можно придумать много разных стратегий. На картинке ниже прямоугольниками обозначены состояния системы, в которых инстанциируются объекты. При уходе из состояния его объекты должны удаляться. У нас в top4top состояния были страницами сайта, иногда - разделами (главная, чат).

Устранение утечки
- В простых случаях достаточно просмотреть код остающихся в памяти классов для устранения ошибки
Если она не очевидна, то тут нам поможет окошко Object References, которое показывает все ссылки на объект и открывается по дабл-клику на элементе в таблице Loitering Objects:

Сложнее всего искать утечки, из-за которых не удаляется вообще ничего. В этом случае два предыдущих способа не работают.
Именно такая ситуация у нас и была. Утечка находилась в часто исползуемом визуальном компоненте (назывался HtmlText). При создании компонента-страницы LayoutIndex в него где-то глубоко добавлялся экземпляр HtmlText и с тех пор вся иерархия (даже после удаления компонента всей страницы из дисплей-листа) становилась неуязвима для Garbage Collector’а.

Способ борьбы тут оказался довольно простой:
- package
- {
- import flash.display.Sprite;
- import flash.events.Event;
- public class test extends Sprite
- {
- private var picture:Picture;
- private var mainText:HtmlText;
- private var enterButton:SomeButton;
- private var closeButton:AnotherButton;
- public function test()
- {
- initContent();
- }
- private function initContent():void
- {
- picture = new Picture();
- addChild(picture)
- // Дальше в том же духе инициализируем и
- // добавляем другие компоненты
- // И подписываемся на удаление со сцены
- addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage, false, 0, true);
- }
- // Удаляем все ссылки на дочерние элементы
- private function onRemovedFromStage(event:Event):void
- {
- picture = null;
- mainText = null;
- enterButton = null;
- closeButton = null;
- while (numChildren > 0)
- removeChildAt(0);
- }
- }
- }
Таким образом в памяти остаются только “текущие” объекты и мы с легкостью с помощью таблицы Loitering Objects их удаляем.
Делать подобные удаления всегда или добавлять этот код только в процессе устранения ликов - это отдельное решение.
Также у нас действует стандарт, что все подписки на события должны быть объявлены с Weak Reference, если нет четких противопоказаний. За весь проект случаев, когда нужна была подписка с жесткой ссылкой было всего несколько. false, 0, true!
Напоследок - регулярное выражение для поиска не-weak подписок: addEventListener\(([\w.\"]*), (\w*)\) (автор: Алексей «Vooparker» Аникутин).
top4top - процесс разработки
Периодически фотографирую интересные моменты нашей работы над порталом top4top.ru, вот два из них:
Алексей «Vooparker» Аникутин, один из авторов yarovoy.com фиксит проблемы upload-а файлов на Mac (если сервер использует переадресацию после окончания загрузки, то на Mac это не работает):

Олег, JAVA-программист, пришел на работу прямо с прибывшего утром проезда:

Диаграммы классов ActionScript 3.0 и Flex - постеры.
Многие в курсе, но тем не менее: в далеком 2006-ом появились диаграммы классов по ActionScript 3.0 и Flex 2 в виде PDF документов, с которых можно напечатать постеры. Фрагмент Flex-диаграммы взят из поста “Ted On Flex”:

ActionScript 3.0 Class Diagram на Flex.org
Flex 2 Diagram на Flex.org
VooDoo подсказывает: Вышеперечисленные + еще куча постеров (Flex 3, AIR)
Напечатали ActionScript 3.0 диаграмму на цветном принтере на А3 с максимальным качеством, получилось мелко, видно только вплотную. Так что если есть возможность, лучше печатать на А2 или А1. На А3 со стандартным качеством вообще ничего не разобрать.
Баги в средствах разработки Flash Platform
Баги есть везде (для справки: шрединбаг, мандельбаг, борбаг), в том числе в mxmlc и Adobe Flex Builder 3. Попробую перечислить опасные и поедающие время баги - а это обычно те, которые абсолютно нелогичны (Ваши варианты в комментах).
- Ранний return в функции: если в начале функции поставить return, то в некоторых случаях это ведет к обрушению компилятора.
Memory-leak при использовании локальной переменной в конструкторе класса - она становится членом класса (гейзенбаг, сейчас повторить не удалось. Источник - Игорь “peace").
- public function ClassConstructor()
- {
- // Иногда при подобном объявлении у экземпляров
- // класса будет появляться поле clip и данный объект
- // не будет собран Garbage Collector'ом.
- var clip:MovieClip = new MovieClip();
- }
Если попробуете запустить профайлер на проект из 1 класса с одной строкой похожего кода в конструкторе то получите Runtime Exception c упоминанием некоего faramir’а
При использовании Embed-метатегов для включения .SWF-файлов в проект встроенные в этот файл шрифты часто (не при каждом запуске одного и того же приложения) заменяют собой аналогичные встроенные в родительский проект.
Например, если у Вас в asset-ах проекта есть надписи статическим текстом со сглаживанием, то при его embed-е Вы сможете пользоваться только теми буквами, что были включены в тот шрифт
(Источник - Филлип “FSB” Бондарев)При загрузке внешнего .SWF-файла с заранее (во Flash IDE) созданными там динамическими текстовыми полями Вам не удаться использовать в них встроенный шрифт основного ролика (Источник - Филлип “FSB” Бондарев).
Это справедливо также для случая, когда Вы вытаскиваете класс символа с существующими там динамическими текстовыми полями через ApplicationDomain.getDefinition() из загруженного файла и инстанциируете его уже в главном проекте
Подборка багов из Adobe Bug and Issue Tracking System
Ошибка компилятора:
- var num = 0.0.8;
Ошибка компилятора:
- function read():int
- {
- return 0;
- }
- function mouseClick(event:MouseEvent):void
- {
- var i: int;
- switch (i = read())
- {
- default:
- do
- {
- i = read();
- }
- while (i != 0);
- trace("OK");
- }
- }
Превращение в бесконечный цикл:
- function makeMeMyError():void
- {
- var a:Array = [];
- var i:uint = 0;
- while (false)
- {
- a[i++]=0;
- continue;
- }
- }
Ошибка компилятора:
- switch (param)
- {
- case 'one' :
- // the following line throws an #1010 Error
- var operations1:XML = company.department.(@title == "Operations")[0];
- trace(operations1);
- break;
- case 'two' :
- // no crash here
- var operations2:XML;
- operations2 = company.department.(@title == "Operations")[0];
- trace(operations2);
- break;
- }
- Пустой свич - ошибка компилятора:
- switch (i)
- {
- }
И финальный аккорд: обрушение браузера / виртуальной машины.
- "".lastIndexOf(null);
P.S: Я рассмотрел только баги компилятора и багоподобные особенности виртуальной машины. Issue по Flex Builder (их сейчас 795) и по другим продуктам к Вашим услугам всегда - Adobe Bug and Issue Tracking System.
Спасибо за наше счастливое детство

Новая версия движка
Все себе попеременял в блоге, если читаете через RSS - буду рад если зайдете посмотреть. Теперь можно изменить padding какого-нибудь блока без давления на мозг Кости
Ура!
Костя, большое спасибо.
Приглашение новым авторам, feedback.
Уважаемые друзья, если у Вас есть что написать, мы будем рады принять Вас в свою компанию riapriority.com. Если у Вас есть тема для поста - пишите, и я попробую ее осветить как смогу ![]()
Использование модулей
Проблема: как загружать код только тогда, когда он нужен? Например, на flash-сайте есть игры, но пользователь может до них и не дойти - поэтому их надо сделать подгружаемыми по требованию.
Решение: Flex project для этого есть модули.
Решение: ActionScript project можно банально подгружать другие .swf и вызывать публичные методы и свойства загруженных объектов, если заранее знать, что они там есть (при соблюдении Security). Мы получаем нетипизированный доступ, со всеми вытекающими из этого негативными последствиями:
- // Вы загрузили игру "Шашки" и теперь хотите ее начать за белых
- (loader.content as Object).start(true);
Есть простой способ сделать внешние модули строго типизированными с богатой историей использования. Пусть мы имеем отдельную .swf с нардами - backgammon.swf, главный класс - Backgammon имплементирующий IBackgammon). В основном проекте оставляем лишь IBackgammon, а сам код игры выносим в отдельный проект.
- // Вы загрузили игру "Нарды" и теперь хотите начать инициатором
- var iBackgammon:IBackgammon = IBackgammon(loader.content)
- iBackgammon.start(true);

Вот в кратце и все. Далее полученный при компиляции игры .swf загружаем во время выполнения (в runtime).
Дополнительно такими манипуляциями мы кроме размера основного .swf уменьшим время компиляции основного проекта.
Особенности: если вы не исключите интерфейс IBackgammon из бинарного файла модуля, то у вас наверняка возникнут ошибки при приведении загруженных данных к этому интерфейсу.
Кстати, в сами нарды вы можете поиграть на нашем портале top4top, в разделе общение. Там вам придется сначала найти себе собеседника и предложить ему партию.
