2010-07-18 25 views
1

Je cherche à construire une classe générique ChargeurXML avec une méthode LOAD statique, avec l'intention de l'utiliser comme suit ...Méthode statique pour la classe XMLLoader ... comment retourner des données XML après la fonction Event.COMPLETE?

private var _data:XML = XMLLoader.LOAD("path/to/xml_file.xml"); 

alors je pourrais l'utiliser dans tout client et aller à la ville avec e4x.

Le problème général que je rencontre concerne l'événement COMPLETE de l'URLLoader, qui appelle nécessairement une fonction différente pour définir les données XML. Cela m'empêche de pouvoir renvoyer le code XML à partir de la méthode LOAD, puisque les données sont définies en dehors de cette fonction. Évidemment, je dois attendre l'événement COMPLETE afin de m'assurer que les données sont disponibles.

Je pensais, peut-être, que je pourrais créer et retourner une fonction _waitForData, qui s'appelle récursivement jusqu'à ce que la donnée soit définie, puis renvoie les données. Mais il semble redondant (puisque Event.COMPLETE le fait de toute façon), et la façon dont je l'ai essayé génère une erreur de débordement de pile.

Heres ce que j'ai essayé:

public class XMLLoader { 

    private static var _url:String = ""; 
    private static var _data:XML = null; 

    public static function LOAD(url:String):XML { 

     _url = url; 
     var _xmlLoader:URLLoader = new URLLoader(); 
      _xmlLoader.addEventListener(Event.COMPLETE, _setData); 
      _xmlLoader.load(new URLRequest(_url)); 

     return _waitForData(); 
    } 

    static function _setData(e:Event):void { 
     XML.ignoreWhitespace = true; 
     _data = new XML(e.target.data); 
    } 

    static function _waitForData():XML { 

     if(_data == null) { 
      _waitForData(); 
     } 

     return _data; 
    } 

    public function LoadXML() {} 

} 

je suis assez nouveau au concept de fonctions récursives, donc j'ai un sentiment que je mis en œuvre ma fonction _waitForData mal (si elle même une bonne idée du tout). J'apprécierais tous les pointeurs sur l'utilisation de méthodes statiques de cette façon, et si ce que j'essaie de faire est possible ... semble être une bonne idée, je pense.

Répondre

4

Vous ne voulez pas utiliser la route tout statique. C'est un gâchis, complique les choses sans nécessité, est difficile à maintenir et si vous voulez charger deux xml en même temps? (indice: vous ne pouvez pas).

Il est préférable de créer une classe régulière qui fait ce que vous voulez (charger un fichier XML et envoyer un événement ou appeler une fonction avec les résultats). Ensuite, vous pouvez ajouter une interface plus simple pour votre indicatif d'appel; Si c'est une fonction statique à utiliser en tant que doublure, alors qu'il en soit ainsi. Cette fonction peut être vue comme une sorte d'usine qui initie crée une instance de la classe et initie l'opération de chargement définit le rappel de résultat, tout en une étape (du point de vue de l'appelant)

Je pense quelque chose le long de ces lignes (juste écrit et compile bien, ne l'ai pas testé à fond, mais devrait être suffisant pour donner l'idée).

package 
{ 
    import flash.events.Event; 
    import flash.events.EventDispatcher; 
    import flash.events.IOErrorEvent; 
    import flash.events.SecurityErrorEvent; 
    import flash.net.URLLoader; 
    import flash.net.URLRequest; 
    import flash.utils.Dictionary; 

    public class XMLLoader extends EventDispatcher 
    { 
     private var _loader:URLLoader; 
     private var _data:XML; 
     private var _callback:Function; 

     private static var _map:Dictionary = new Dictionary(); 

     public function XMLLoader(callback:Function = null) 
     { 
      _data = null; 
      _callback = callback; 
      _loader = new URLLoader(); 
      _loader.addEventListener(IOErrorEvent.IO_ERROR,handleError); 
      _loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,handleError); 
      _loader.addEventListener(Event.COMPLETE,handleComplete); 
     } 

     public function load(request:URLRequest):void { 
      _loader.load(request); 
     } 

     private function dispatchError():void { 
      cleanUp(); 
      dispatchEvent(new Event(Event.COMPLETE)); 
      if(_callback is Function) { 
       _callback(null); 
      } 
     } 

     private function dispatchSuccess():void { 
      cleanUp(); 
      dispatchEvent(new Event(Event.COMPLETE)); 
      if(_callback is Function) { 
       _callback(_data); 
      } 
     } 

     private function handleError(e:Event):void { 
      dispatchError(); 
     } 

     private function handleComplete(e:Event):void { 
      var success:Boolean = false; 
      try { 
       _data = new XML(e.target.data); 
       success = true; 
      } catch(err:TypeError) { 
       success = false; 
      } finally { 
       if(success) { 
        dispatchSuccess(); 
       } else { 
        dispatchError(); 
       } 
      } 
     } 

     public function get data():XML { 
      return _data; 
     } 

     private function cleanUp():void { 
      if(_loader) { 
       _loader.removeEventListener(IOErrorEvent.IO_ERROR,handleError); 
       _loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR,handleError); 
       _loader.removeEventListener(Event.COMPLETE,handleComplete); 
      } 
      if(_map[this]) { 
       delete _map[this]; 
      } 
     } 

     public static function loadXml(url:String,callback:Function):XMLLoader { 
      var loader:XMLLoader = new XMLLoader(callback); 
      loader.load(new URLRequest(url)); 
      // pin down the instance just to be safe; I've seen loaders being collected... 
      _map[loader] = true; 
      return loader; 
     } 

    } 
} 

Utilisation:

private function handleResult(data:XML):void { 
     if(data) { 
      trace(data); 
     } else { 
      trace("failed"); 
     } 
    } 


    private function test():void { 
     XMLLoader.loadXml("your.xml",handleResult); 
    } 

Sinon, vous pouvez passer à utiliser les méthodes d'instance, si vous avez besoin plus de flexibilité:

private function test():void { 
     var loader:XMLLoader = new XMLLoader(handleResult); 
     loader.load(new URLRequest("your.xml"));   
    } 

Cette mise en œuvre de l'échantillon peut être utilisé avec rappel ou d'événements . Pour simplifier, je définis simplement une fonction de rappel; vous pourriez dire si l'opération de chargement a réussi en vérifiant les données que vous recevez; null signifie qu'il a échoué. En outre, j'expédie un événement Event.COMPLETE pour signaler que le chargeur a terminé le chargement (même s'il a échoué); encore une fois, vous pourriez savoir si elle a échoué ou réussi en vérifiant la propriété de données. Vous pouvez utiliser des événements personnalisés si vous voulez et/ou définir un rappel de panne, mais je pense que vous voulez une interface aussi simple que possible, donc je pense que cela devrait suffire.

+0

Ceci est un exemple EXTRÊMEMENT instructif, bien au-delà de la réponse que j'espérais. J'étudierai minutieusement vos commentaires et mon exemple de code et suggérerai aux autres de le faire aussi. Merci! –

+0

@Logic Artist. Pas de problème, je suis content que vous trouviez mon anwser utile. –

0

Vous demandez trop au système, le flash n'a pas été conçu pour cela. Vous ne pouvez pas (ou ne devriez pas) suspendre tout le système pour attendre un résultat. Ecrire en flex/flash nécessite d'utiliser des gestionnaires de complétion car dans certains cas, comme ça, il n'y a aucun moyen de contourner le problème. Si vous faisiez les choses de cette façon, les images ne se déplaceraient jamais, et tout ce qui se passait dans le programme s'arrêterait brutalement jusqu'à ce que la chose revienne, rendant une expérience utilisateur très malheureuse qui aurait l'air de "se bloquer". Sur le plan technique, si vous avez absolument insisté sur ce point, vous devrez (1) trouver un moyen de mettre un délai dans cette boucle if-null, et (2) le changer en while(_data != null){ /* wait a while */ } mais je vraiment ne recommande pas de descendre ce chemin. Plus précisément, vous devez être prudent lorsque vous appelez une fonction à l'intérieur de lui-même. Dans votre cas, _waitForData demande _waitForData pour demander _waitForData pour demander _waitForData à ... etc, jusqu'à ce qu'il atteigne une limite arbitraire et s'arrête. C'est ce qu'on appelle infinite recursion.

Solution: Vous devez structurer votre programme de façon à ce qu'il cesse de faire ce qu'il fait lorsqu'il a besoin de charger quelque chose, et qu'il prenne ce dont il a besoin quand un gestionnaire d'achèvement est appelé.

Mise à jour:

Solution 2: Écrivez une classe qui gère tout le chargement XML dont vous avez besoin. Vous pouvez le faire pré-remplir les choses au début du programme si vous savez ce qui pourrait être demandé. Puis, comme vous en avez besoin de plus, demandez à la classe pour plus. Ensuite, s'il l'a déjà chargé, il peut simplement le remettre immédiatement. S'il ne l'a pas, il renverra null, le récupèrera en arrière-plan et appellera un gestionnaire quand c'est fait.De cette façon, le code qui l'appelle peut au moins essayer de faire des choses avec le XML immédiatement, mais à la fin, il devra toujours gérer le cas pour quand il n'est pas chargé.

+0

Assez juste ... J'ai pensé que la boucle récursive était une mauvaise idée. Cependant, le problème principal n'est pas vraiment que la boucle arrête le programme (c'est juste un effet secondaire d'une mauvaise solution) ... c'est que j'ai besoin d'un moyen de retourner les données de la fonction statique qui utilise un gestionnaire d'événements. Merci quand même. –

+0

renvoie les données à qui? La fonction doit-elle être statique? – eruciform

+0

Renvoyez les données au contexte dans lequel elles sont appelées. L'idée est de passer une URL à la méthode (comme démontré ci-dessus), sans avoir à instancier la classe. Pensez, par exemple, à la classe Math de Flash, transmettez simplement une valeur à l'une de ses méthodes statiques et elle renvoie la réponse. Bien sûr, il ne doit pas être statique, mais de cette façon, je peux créer une méthode simple, réutilisable, d'une seule ligne. –