Flex Modules – L'Event ModuleEvent.READY n'est jamais envoyé [Résolu]
Ce billet est consacré à la résolution d'un bug Flex qui se produit pendant le chargement des modules. Il est plutôt étrange et difficile à débugger. Ce problème est du au Garbage Collector qui va un peu trop vite en besogne lorsque l'on charge des modules Flex avec la classe IModuleInfo.
Voici un exemple de code qui permet de charger un module:
public function initApp():void {
var moduleInfo:IModuleInfo = ModuleManager.getModule("SimpleModule.swf");
moduleInfo.addEventListener(ModuleEvent.READY, onModuleLoaded);
moduleInfo.load();
}
Dans l'esprit, ce code fonctionne bien et une fois le module chargé, la méthode onModuleLoaded devrait être appelée. Et bien à cause du GC, l'évènement ModuleEvent.READY ("ready") n'est jamais renvoyé. Apparemment, ce bug se produit sur Windows mais pas sous Unix, surement à cause d'une différence de Garbage Collector. Le problème est en fait un problème de scope. Même si la variable "moduleInfo" a des références vers elle (ne serait-ce que par l'évènement ModuleEvent.READY qui crée une back reference), la variable moduleInfo va aléatoirement être supprimée de la mémoire. l'évènement "ready" ne sera donc jamais envoyé.
Résolution du problème
Pour résoudre ce problème, il suffit de mettre la variable dans un scope au niveau de la classe et pas de la méthode:
private var moduleInfo:IModuleInfo=null;
public function initApp():void {
moduleInfo=ModuleManager.getModule("SimpleModule.swf");
moduleInfo.addEventListener(ModuleEvent.READY, onModuleLoaded);
moduleInfo.load();
}
De cette manière, la variable ne sera pas détruite et l'évènement READY sera bien envoyé dans tous les cas.
D'autres bugs possibles…
Le fait que l'évènement READY ne soit jamais dispatché est un des effets de ce problème de scope. Il se peut que vous ayez les problèmes suivants:
- Le module ne se charge pas la première fois mais se charge bien la deuxième fois
- La méthode ModuleInfo.clearLoader() lance une Error #2029: This URLStream object does not have a stream opened when calling loader.close().
La cause interne du problème
Si vous êtes curieux, cet article explique bien la cause de ce problème. Le problème se passe lorsque le Loader finit de charger le SWF La liste interne de listeners du ModuleInfo (les listeners ajoutés pendant la construction du ModuleInfoProxy) est perdue (null) quand le ModuleInfo atteint sa méthode readyHandler(). A la fin de la méthode readyHandler(), un évènement ModuleEvent.READY est dispatché:
dispatchEvent(new ModuleEvent(ModuleEvent.READY));
Lorsque la liste de listener est null, l'évènement n'est jamais récupéré par le ModuleInfoProxy et le chargement échoue.
Lors des prochains chargements (la deuxième fois au moins), la méthode load() du ModuleInfoProxy regarde si la variable "info" a déjà été chargée et propage directement l'évènement ModuleEvent.READY:
else if (info.loaded){
//trace("Module[", url, "] load is already loaded");
if (info.setup){
...
if (info.ready){
...
dispatchEvent(new ModuleEvent(ModuleEvent.READY));
}
}
}
Voilà pourquoi le module se charge bien la seconde fois.
Articles similaires
- Flex Modules – Utiliser ModuleManager pour charger des modules
- Flex Modules – Pré-chargement de modules (preloading)
- Flex Modules – Communication Application vers Module
- Flex Modules – Utiliser l'évènement Error (ModuleEvent.ERROR)
- Flex Modules – Chargement de Modules depuis des serveurs différents (allowDomain)
Aucun trackbacks pour l'instant






19 novembre 2009
Merci pour le tip mais cela me pose un problème quand j'essai de charger 2 modules à la suite l'un de l'autre. J'imagine que cela est du au fait que l'on utilise une seule variable pour lancer le chargement du module. Dans mon cas je ne passe dans l'évènement MduleEvent.Ready uniquement pour mon 2ème module. Quelle est la solution pour contourner ce problème?
Merci d'avance
19 novembre 2009
Salut,
malgré l'absence de code, je pense que tu lances deux load() successifs sans attendre que le premier soit chargé. La variable est donc "écrasée" par le dernier module. Il faut que tu fasses le chargement de manière synchrone si tu utilises une seule variable (une sorte de queue). Une fois que le premier module est chargé (évènement READY), tu lances le chargement du prochain module. C'est une solution, tu peux assi créer deux variables (pas très propre).
Fabien
24 février 2010
Salut.
).
Quelqu'un aurait-il un exemple pour faire cette "sorte de queue" ?
En fait, je dois charger X modules (je ne connais pas le nombre à l'avance, c'est défini dans un fichier de configuration de mon appli, en gros), et j'aimerais savoir quand est-ce que tous est chargé et prêt à être utilisé.
Je fais une boucle qui lance les load(), donc je tombe sur le pb de gille78.
J'envisage peut être un truc récursif (rappeler le load sur la reception de ModuleEvent.ready, mais j'ai pas mal de doutes, car j'ai l'impression qu'il perd un peu les pédales, ou bien c'est moi, ou bien les deux
Donc, si quelqu'un a deja contourné ça, je prends !
Merci
24 février 2010
salut,
c'est plutot simple, tu as ton tableau de N urls à charger.
Tu en charges une (la première), quand c'est fini, tu fait un shift() sur ton tableau (pour supprimer l'url que tu viens de charger et tu repasses dans ta méthode (qui va charger la première url, qui n'est maintenant plus la même), etc.
Quand ton tableau est vide, tu as fini ton chargement et voilà
Fait attention aux erreur quand même, qu'il fassent bien le shift pour passer au chargement du module suivant
Fabien
25 février 2010
salut.
La j'étais en plein mode "pourquoi faire simple" …
Effectivement, j'étais partit sur une usine à gaz !
Je tente ça de suite.
Merci
26 avril 2010
Bigup fnicollet !
J'étais en train de m'arracher les cheveux sur ce problème …
Merci pour ce post !