PDA

Просмотр полной версии : старнный эксепшен при использовании CustomItemRenderer


erectus
01.12.2009, 16:55
Подскажите где искать проблему, уже всё перерыл....
Структура приложения следующая. Есть основная swf-ка, содержашая основные классы и минимум ресурсов (чтобы грузилась быстрей). В нее динамически через несколько SWFLoader-ов подгружаются отдельные модули. Один из таких модулей - магазин. Основная его часть - это два флексовских контрола: List - список товаров, и TileList - инвентарь пользователя. Для обоих списков созданы свои ItemRenderer-ы: для товара он содержит картинку, описание товара и цену, плюс кнопку "купить", при нажатии на которую на сервер отправляется соотвсетствующая команда. В ответ сервер присылает список элементов инвентаря с учетом купленной вещи. Этот ответ принимается главной swf-кой, которая генерит евент. Модуль "магазин" ловит этот эвент и обновляет инвентарь (создается новый ArrayCollection, заполняется данными и заменяется dataProvider).
похоже, что примерно в этот момент вываливается следующее исключение:
TypeError: Error #1009: Не удается вызвать свойство или метод со ссылкой на объект "null".
at mx.utils::LoaderUtil$/normalizeURL()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\utils\LoaderUtil.as:93]
at mx.controls::SWFLoader/loadContent()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\SWFLoader.as:1626]
at mx.controls::SWFLoader/load()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\SWFLoader.as:1447]
at mx.controls::SWFLoader/commitProperties()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\SWFLoader.as:1229]
at mx.core::UIComponent/validateProperties()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\core\UIComponent.as:5807]
at mx.managers::LayoutManager/validateClient()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\managers\LayoutManager.as:811]
at mx.controls.listClasses::TileBase/getPreparedItemRenderer()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\listClasses\TileBase.as:585]
at mx.controls.listClasses::TileBase/makeRowsAndColumns()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\listClasses\TileBase.as:409]
at mx.controls.listClasses::ListBase/makeRowsAndColumnsWithExtraRows()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\listClasses\ListBase.as:1380]
at mx.controls.listClasses::ListBase/updateDisplayList()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\listClasses\ListBase.as:3726]
at mx.controls.listClasses::TileBase/updateDisplayList()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\listClasses\TileBase.as:2357]
at mx.controls.listClasses::ListBase/validateDisplayList()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\controls\listClasses\ListBase.as:3348]
at mx.managers::LayoutManager/validateDisplayList()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\managers\LayoutManager.as:622]
at mx.managers::LayoutManager/doPhasedInstantiation()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\managers\LayoutManager.as:695]
at Function/http://adobe.com/AS3/2006/builtin::apply()
at mx.core::UIComponent/callLaterDispatcher2()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\core\UIComponent.as:8733]
at mx.core::UIComponent/callLaterDispatcher()[C:\autobuild\3.4.0\frameworks\projects\framework\src\mx\core\UIComponent.as:8673]

Все, что нашел в гугле, это совет инициализировать переменные:

when defining my private variables, instead of writing:
private var textfield:TextField;

I had to write
private var textfield:TextField = new TextField();


но где искать, если эксепшен вываливается в mx.utils::LoaderUtil.normalizeURL()?

Elfenit
01.12.2009, 17:03
Запостите хотябы текст виноватого по вашему мнению рендерера. Эксепшн где-то в нутрях валится, поэтому если виноват именно рендерер, значит он неправильно написан =)
Да и вообще, вы уверены в виновности рендерера? Зачем рендереру нужен вдруг метод normalizeURL()?

erectus
01.12.2009, 18:33
Зачем рендереру нужен вдруг метод normalizeURL()?
я думаю, URL - адрес файла с изображением...

код рендерера, пожалуйста:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
xmlns:mx="http://www.adobe.com/2006/mxml"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
backgroundImage="assets/inventory/cell.png"
initialize="init()"
mouseOver="showButtons()"
mouseOut="hideButtons()"

toolTip="_"
toolTipCreate="createToolTip(event)"
>

<mx:Image id="mImage" source="{mImageSource}" />

<mx:Button id="btnUse"
x="0"
y="0"
width="{BUTTON_SIZE}"
height="{BUTTON_SIZE}"
icon="@Embed(source='../../../../../resources/btn_useitem.png')"
toolTip="{resourceManager.getString('strings', 'BTN_USE')}"
click="onBtnUseClick()"
/>

<mx:Button
id="btnSell"
x="{BUTTON_SIZE + BUTTON_GAP}"
y="0"
width="{BUTTON_SIZE}"
height="{BUTTON_SIZE}"
icon="@Embed(source='../../../../../resources/btn_sellitem.png')"
toolTip="{resourceManager.getString('strings', 'BTN_SELL')}"
click="onBtnSellClick()"
/>

<mx:TextArea id="lbAmount"
x="0"
y="{this.height - BUTTON_SIZE}"
width="{BUTTON_SIZE}"
height="{BUTTON_SIZE}"
text="{mAmount}"
selectable="false"
backgroundColor="#AAAAAA"
backgroundAlpha="0.7"
fontWeight="bold"
color="#0000FF"
/>

<mx:Script>
<![CDATA[

import MiniIT.projects.petwar.net.IModuledServer;
import MiniIT.projects.petwar.ui.CustomToolTip;
import mx.events.ToolTipEvent;

private const BUTTON_SIZE : int = 20;
private const BUTTON_GAP : int = 3; // расстояние между кнопками

private var mShowUseButton : Boolean = false;
private var mShowSellButton : Boolean = false;

[Bindable]
private var mImageSource : * ;

[Bindable]
private var mAmount : String;

private function init():void
{
mouseChildren = true;
hideButtons();
}

override public function set data( value:Object ):void
{
super.data = value;
trace("-- inventory ir set data --", data.type, data.image);
switch(data.type)
{
case "clothing":
case "food":
case "toy":
case "property":
case "spystuff":
mShowUseButton = true;
mShowSellButton = (data.price > 0);
break;

default:
mShowUseButton = false;
mShowSellButton = false;
toolTip = "";
break;
}

if (data && data.id >= 0 && data.id != int.MAX_VALUE)
{
if(data.image)
mImageSource = data.image;
if (data.amount)
mAmount = data.amount.toString();
}
else
{
mImageSource = "/assets/inventory/empty.png";
mAmount = "";
}
}

private function showButtons():void
{
btnUse.visible = mShowUseButton;
btnSell.visible = mShowSellButton;
}

private function hideButtons():void
{
btnUse.visible = false;
btnSell.visible = false;
}

private function createToolTip(event:ToolTipEvent) : void
{
if (data && data.name)
{
var tip : InventoryToolTip = new InventoryToolTip();
tip.data = data;
event.toolTip = tip;
}
}

private function onBtnUseClick():void
{
Global.server.activateItem(data.id);
}

private function onBtnSellClick():void
{
Global.server.shop.sell(data.id);
}

]]>
</mx:Script>
</mx:Canvas>

Elfenit
01.12.2009, 18:49
в set data самой первой строчкой припишите
if (!data) return;
Или вставьте везде проверки на не налл дату. А то вот такая проверка if (data && data.id >= 0 && data.id != int.MAX_VALUE) выглядит довольно глупо после уже неоднократного обращения к полям data.
Дальше посмотрим.

Кстати, не увидел в коде нормалайза никакого.
Плюс, какую SDK вы используете для сборки? В 3.4 в этой LoaderUtil.as:93 строке не содержится опасных операций + 3.4 не выдает ошибок на русском, как я знаю.

erectus
01.12.2009, 19:14
Кстати, не увидел в коде нормалайза никакого.
ну так в этом и вопрос: где искать проблему :)

какую SDK вы используете для сборки?
3.4.1.10084

3.4 не выдает ошибок на русском, как я знаю
насколько я понимаю, русский текст выдает не SDK, а плеер, в зависимости от системных насроек... хотя я могу и ошибаться

Elfenit
01.12.2009, 19:17
Ну ок. Так что-то изменилось после такого изменения кода?

erectus
01.12.2009, 20:31
нет, ничего не изменилось :(

wvxvw
01.12.2009, 21:02
>> какую SDK вы используете для сборки?
3.4.1.10084

Чего-то не срастается... ... autobuild\3.4.0\frameworks...
Да и скорее всего что-то с настройками загрузки RSL - в ваших модулях, скорее всего что етот рендерер тут ни при чем.

Elfenit
01.12.2009, 21:12
<mx:Image id="mImage" source="{mImageSource}" />
По-моему, так нельзя. http://www.adobe.com/livedocs/flex/3/langref/mx/controls/Image.html

Больше существенных нареканий к этому коду кроме архитектуры у меня нет. Если исправление или временное отключение этого участка не поможет, я бы стал искать проблему где-то вокруг.

erectus
01.12.2009, 22:33
>> какую SDK вы используете для сборки?
3.4.1.10084

Чего-то не срастается... ... autobuild\3.4.0\frameworks...
да... меня это тоже смутило, но тем не менее
я вообще удалил старую версию (3.4.0), заменил ее новой и перекомпилил, но сообщения об ошибке те же

Да и скорее всего что-то с настройками загрузки RSL - в ваших модулях,
я этого не исключаю, поэтому и расписал всю структуру... хотя я делал всё по adobe-всим туториалам

Добавлено через 2 минуты
<mx:Image id="mImage" source="{mImageSource}" />
По-моему, так нельзя. http://www.adobe.com/livedocs/flex/3...ols/Image.html

в других местах такой код работает без проблем и нареканий

Если исправление или временное отключение этого участка не поможет, я бы стал искать проблему где-то вокруг.
я когда-то пробовал закомментировать какой-то участок типа этого и проблема исчезала... поэтому я и грешу на рендерер... попробую еще раз найти этот кусок

wvxvw
01.12.2009, 22:38
Ну скомпилируйте с -keep посмотрите, чго в SystemManager в качестве путей к RSL попало... если компилируете весь проект с модулями из Flex / FlashBuilder то он это делает очень плохо и непонятно - лучше потратить немного времени и написать Ант скрипт, который бы компилировал весь набор, чем надеятся на билдер, особенно если компилируется несколько проектов одновременно.

EDIT: Да, еще вариант - скопируйте к себе в рабочую директорию LoaderUtil (чтобы при компиляции подхватился именно ваш, а не фреймворковский) и поставьте в нем трейсы - хоть будете знать изза чего именно ошибка.
Да, и еще, в некоторых версиях билдера дебаггер мог показывать фреймворковкие сорцы если там были ошибки - ну, соответственно, можно попробовать дебаггером выяснить, где там чего его не устроило.

erectus
01.12.2009, 23:12
я FlashDevelop использую =)

wvxvw
02.12.2009, 00:45
Ну -keep и monkey-patch'и никто не отменял.

erectus
02.12.2009, 17:17
вот что удалось выяснить...
скопировал я по вашему совету LoaderUtil в свою рабочую директорию, а за одно и SWFLoader, т.к. вызовы идут от него. понаставил трейсов.

public static function normalizeURL(loaderInfo:LoaderInfo):String
{
trace("__ LoaderUtil.normalizeURL(loaderInfo) loaderInfo =", loaderInfo);
trace("___loaderInfo.url =", loaderInfo.url);
.....
}

выяснилось, что loaderInfo вседгда валидный объект, а вот loaderInfo.url как раз и является причиной исключения.

в SWFLoader нахожу кусок кода, который вызывает LoaderUtil.normalizeURL и непосредсвенно перед вызовом ставлю трейс:

....

// make relative paths relative to the SWF loading it, not the top-level SWF
if (!(url.indexOf(":") > -1 || url.indexOf("/") == 0 || url.indexOf("\\") == 0))
{
var rootURL:String;
if (SystemManagerGlobals.bootstrapLoaderInfoURL != null && SystemManagerGlobals.bootstrapLoaderInfoURL != "")
rootURL = SystemManagerGlobals.bootstrapLoaderInfoURL;
else if (root)
{
trace("__ SWFLoader root =", root);
rootURL = LoaderUtil.normalizeURL(root.loaderInfo);
}
else if (systemManager)
rootURL = LoaderUtil.normalizeURL(DisplayObject(systemManager).loaderInfo);

if (rootURL)
url = LoaderUtil.createAbsoluteURL(rootURL, url);
}

.....

получаем такой вывод трейса:

__ SWFLoader.load(url) url = ../assets/inventory/property01.png
......
__ SWFLoader root = [object _Pers_mx_managers_SystemManager]
__ LoaderUtil.normalizeURL(loaderInfo) loaderInfo = [object LoaderInfo]
__ loaderInfo.url = null

во-первых эксепшен валится именно при загрузке изображения для ItemRenderer-a.
во-вторых напрягает, что root - экземпляр класса _Pers_mx_managers_SystemManager. Я так полагаю что это автоматически созданный SystemManager для модуля Pers.swf, который грузится первым в тот SWFLoader, который позже подгружает модуль магазина. и модуль "Pers" также содержит инвентарь (тот самый злополучный TileList) с тем же самым ItemRenderer-ом.

пытался указать разные классы рендереров для разных модулей, не помогло.
также пытался создать экземпляр рендерера в главном приложении, в надежде, что его(рендерера) root-ом станет не именно главный модуль приложения... тоже ничего не изменилось
куда еще можно копнуть?

wvxvw
02.12.2009, 19:44
O_o, а как такое вообще может быть, чтобы у загруженной SWF loaderInfo.url был null?
Я так подозреваю, что предыдущий модуль не выгружается... причин может быть оочень много...
Ну, даже не знаю, а что будет если использовать 2 лоадера? Ну, я понимаю, что не лучший вариант, но искать почему предыдущий модуль не выгрузился может быть очень сложно.

erectus
04.12.2009, 15:57
Да, 2 лоадера действительно помогли... но это очень уж кривое решение. У меня в этот лоадер в зависимости от действий пользователя должны постоянно подгужаться 8 разных модулей... не создавать же 8 лоадеров... может есть идеи как это обойти?

Добавлено через 1 час 3 минуты
мне удалось воспроизвести эту ситуацию в отдельном приложении.
скачать можно тут (http://www.**************/?kmqzwnz0gzz)
может это поможет

ЗЫ: там много что сделано криво... это только для теста.... особенно криво сделано ожидание инициализации загруженного в лоадер приложения... где-то в инете видел более правильное решение (листенер к SystemManager, не помню на какой эвент), не могу найти... может подскажите

Добавлено через 1 час 55 минут
Похоже, что решение найдено!!!
Вместо SWFLoader-ов использовать ModuleLoader-ы, ну и соответственно подгружаемые модули переклепать с Application на Module.
В этом случае проблема пропадает... но почему-то сильно увеличились задержки при динамической загрузке изображений внутри модулей, где связь?

wvxvw
04.12.2009, 18:38
Кстати, да, замечал такое... в итоге грузил картинки сам и отрисовывал их в graphics UIComponent'a, как бы глупо, но, на столько заморачиваться с вопросами типа "почему?" во фреймворке - только себе нервы портить и время даром терять :)