Generalnie, flex jako flex jest bardzo fajnym narzędziem, ale jak każda technologia ma trochę problemów. Ja ostatnio natrafiłem na problemy związane z podłączeniem modułu który komunikował się za pomocą WebService. Warto nadmienić, że sam moduł, został przygotowany nie jako moduł, ale jako osobna aplikacja webowa, co dostarczyło mi dodatkowych problemów. Przejdźmy do sedna:
Moduły/Aplkiacje flexowe można ładować na kilka sposobów. Po pierwsze możemy skorzystać z loadera i przy jego pomocy załadować aplikację. Przykładowy kod wygląda następująco:
MXML:
<mx:moduleloader url="SomeModule.swf"/>
AS:
var loader : ModuleLoader = new ModuleLoader();
loader.url = "SomeModule.swf";
contentPanel.addChild(loader);
Sprawy oczywiście się dalej komplikują, bo jak można się domyślić, nie jest to jedyny sposób ładowania modułów. Po pierwsze możemy użyć różnych loaderów. Dla przykładu SwfModuleLoader nadaje się do załadowania aplikacji a nie modułu (jak również swf wykonanego we flashu). Zastosowanie tego loadera potrafi rozwiązać wiele problemów które można spotkać z innymi loaderami, w szczególności można przy jego pomocy prawidłowo ustawić moduł na ekranie. Obsługuje on również eventy związane ze zmianą wielkości obszaru aplikacji. Użycie tego loadera rozwiązało moje problemy związane z ładowaniem modułu (o których napiszę dalej).
Aby sprawy dalej skomplikować, można również użyć ModuleManager, co zostało zastosowane w przypadku aplikacji do której się podłączałem. Stosowanie ModuleManager wymaga jednak nieco innego podejścia. Jest to element który zarządza załadowanymi modułami. Przykładowy kod zastosowania wygląda następująco:
var moduleInfo : IModuleInfo;
moduleInfo = ModuleManager.getModule(_url);
moduleInfo.load();
var module:Object = moduleInfo.factory.create();
container.addChild(module);
Jak widać, flex pozwala na na prawdę dużą dowolność. W szczególności można użyć po prostu Loader. W zasadzie nie było by to aż tak skomplikowane, gdyby nie fakt, że dodatkowo jest jeszcze takie pojęcie jak ApplicationDomain. Domena aplikacji de-facto oznacza, w jakim środowisku dany moduł powinien zostać załadowany. Domenę ładuje się następująco:
var applicationDomain:ApplicationDomain = ...;
-----------------------------------------------------
var moduleInfo : IModuleInfo;
moduleInfo = ModuleManager.getModule(_url);
moduleInfo.load(applicationDomain);
-----------------------------------------------
var loader : ModuleLoader = new ModuleLoader();
loader.url = "SomeModule.swf";
contentPanel.addChild(loader, applicationDomain);
Pod zmienną applicationDomain z przykładu można podstawić 3 (w zasadzie 4) wartości. Pierwsza (domyślna) to wartość:
var applicationDomain:ApplicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
lub
var applicationDomain:ApplicationDomain = null;
Tego typu zapis oznacza, że moduł jest modułem dzieckiem. Do rodzica można dostać się poprzez ApplicationDomain.parrentDomain. Niestety, jest to tylko teoria. Obie domeny co prawda są odseparowane, ale jeżeli korzystamy w naszej aplikacji z singletonów, to będą się one zachowywać na dwa sposoby. Albo będą modułowe, albo aplikacyjne. Jeżeli singleton nie został zainicjalizowany w aplikacji (a wystarczy tylko wspomnienie takie jak var sig:SomeSingleton = null), to jego zasięg będzie dotyczył tylko modułu (nawet, jeżeli występuje w innych modułach). Natomiast, jeżeli został zainicjalizowany w aplikacji, to będzie to singleton aplikacyjny. Przypomnę, że w moim wypadku nie miałem dostępu do kodu aplikacji, a więc nie byłem w stanie sprawdzić, czy singleton którego używam, był użyty w aplikacji.
Samo to nie jest jeszcze straszne, ale są dalsze konsekwencje. Może wystąpić sytuacja, w której sigleton dynamicznie powołuje do instnienia obiekty klas (po nazwie klasy). Takie zachowanie ma na przykład SchemaTypeRegistry.getInstance().registerClass(...) używane do rejestrowania klas dla WebService. Nagle pojawia się Error #1065, oznaczający, że nie można załadować klasy, ponieważ nie została ona dołączona do skompilowanego kodu. Dokładnie rzecz ujmując ApplicationDomain znajdujący się w aplikacji (w przypadku singletona aplikacyjnego) próbuje powołać do życia obiekt, dla którego klasa znana jest tylko w ApplicationDomain modułu. Osobiści podejrzewam, że jest to jakiś błąd w flexie, ale jak szukałem informacji na temat, to przekrój czasowy w jakim można znaleźć posty na ten temat jest imponujący (2005-2010). Nawet Flex 4 posiada bardzo podobnie wyglądający błąd.
Jak pisałem, są jeszcze 2 warianty przypisania AppliacationDomain:
var applicationDomain:ApplicationDomain = new ApplicationDomain();
oraz
var applicationDomain:ApplicationDomain = ApplicationDomain.curentDomain;
Pierwszy sposób tworzy całkowicie odrębną domenę aplikacji. Osobiście tematu nie zbadałem, ale to co mogę powiedzieć, to to, że aplikacja nie chciała mi się uruchomić z modułem, musiałem zmienić moduł na aplikację, żeby osiągnąć jakikolwiek efekt.
Ostatni sposób, tworzy wspólną domenę dla modułu oraz aplikacji. Takie podejście powoduje, że nagle klasy których mi brakowało, znajdują się w tej samej domenie co aplikacja. Oznacza to, że mój problem przestaje istnieć, jednak warto wspomnieć, że nie ma również separacji modułu i aplikacji.
Więcej informacji można znaleźć w tych artykułach:
http://livedocs.adobe.com/flex/3/loading_applications.pdf
http://livedocs.adobe.com/flex/3/html/help.html?content=modular_5.html
http://life.neophi.com/danielr/2006/07/flex_2_runtime_error_1065.html