Ce tutoriel explique comment utiliser le framework de préchargement en masse (MassLoader) téléchargeable ci-dessous dans une animation flash. Afin de comprendre le fonctionnement, je vous conseille de bien connaître les subtilités relatives aux chargement de fichiers (URLLoader, URLRequest, Loader, LoaderContext).
C'est maintenant un projet OpenSource ouvert ici du nom de masapi qui a repris le flambeau de ce framework.
Charger un fichier texte, xml, image ou swf est simple avec AS3 et vous avez plusieurs manière de le faire, que ce soit via Loader (pour les fichier binaires) ou URLLoader (pour tous les types de fichier). Cela devient un peu plus compliqué à gérer lorsqu'il s'agit de charger toute une liste de fichier. De plus il arrive souvent que ceux-ci soient inter-dépendants.
Imaginons un site web en flash : il arrive très souvent que l'orsqu'un internaute demande d'afficher une page, plusieurs fichiers doivent être chargés (les données issues d'un php, le swf d'affichage, des images, …). Comment faire en sorte que tout cela devienne beaucoup plus simple pour nous autres développeurs ?
Le petit framework téléchargeable ci-dessous vous permet justement de charger des fichiers en masse très simplement. Cela se passe en deux partie :
Pour utiliser les exemples montrés ci-dessous, il faut télécharger le framework MassLoad suivant : MassLoad API 2.0
kézako ? Afin de pouvoir utiliser le plus simplement possible le MassLoader, il faut utiliser un type standard. Celui-ci est représenté par l'interface ILoadableFile qui permet d'utiliser n'importe quel type de gestionnaire de chargement URLLoader ou Loader. Pour créer un fichier à télécharger, voici la procédure à suivre :
import ch.capi.net.*; var factory:LoadableFileFactory = new LoadableFileFactory(); var file1:ILoadableFile = factory.create("data/file.txt");
Le code ci-dessus va nous créer automatiquement un object ILoadableFile qui utilisera un objet URLLoader afin de charger les données. L'utilisation de la classe URLLoader est définie par l'extension du fichier. Toutefois il est aussi possible de choisir manuellement quel gestionnaire de chargement utiliser :
import ch.capi.net.*; import flash.net.*; import flash.display.*; var factory:LoadableFileFactory = new LoadableFileFactory(); var file1:ILoadableFile = factory.createURLLoaderFile(new URLRequest("data/file.txt")); //utilisation d'un URLLoader var file2:ILoadableFile = factory.createLoaderFile(new URLRequest("data/file.swf")); //utilisation d'un Loader //récupération des gestionnaires de chargement var loader1:URLLoader = file1.loadManagerObject; var loader2:Loader = file2.loadManagerObject;
Les gens qui ont jeté un coup d'oeil à la documentation auront noté que les objets de type ILoadableFile possèdent une propriété virtualBytesTotal qui peut sembler très abstraite. Celle-ci sert au pré-calcul des bytes total afin que le MassLoader puisse fournir une évaluation du volume total des données à charger.
Par défaut cette valeur est fixée à 204800 bytes, ce qui correspond à 200ko. Elle est attribuée à tous les fichiers créés par la classe LoadableFileFactory et peut être modifiée de la sorte :
import ch.capi.net.*; var factory:LoadableFileFactory = new LoadableFileFactory(102400); var file1:ILoadableFile = factory.create("data/file.txt"); trace(file1.virtualBytesTotal); //102400
Nous allons voir par la suite quelle est l'utilité de cette propriété !
Ceci est simplement la classe qui va gérer le chargement de tous les fichiers se trouvant dans sa liste. Il s'agit de la classe MassLoader qui permet de charger les fichiers de manière parallèle (plusieurs fichiers à la fois) ou séquentielle (1 à 1).
Une fois que nos fichiers ont été créés, il suffit de les ajouter à la liste de chargement et de lancer le tout :
import ch.capi.net.*; //création des fichiers var factory:LoadableFileFactory = new LoadableFileFactory(); var file1:ILoadableFile = factory.create("data/file1.txt"); var file2:ILoadableFile = factory.create("data/file2.txt"); var file3:ILoadableFile = factory.create("swf/file.swf"); var file4:ILoadableFile = factory.create("xml/file.xml"); //création du loader var loader:MassLoader = new MassLoader(); //ajout des fichiers dans la liste de chargement loader.addFile(file1); loader.addFile(file2); loader.addFile(file3); loader.addFile(file4); //lancement du chargement loader.start();
Il est possible de spécifier le nombre de fichier à télécharger en même temps. Dans l'exemple ci-dessus, aucune valeur n'étant spécifiée, le chargement de tous les fichier en parallèle va être lancé. Quelques exemples (il est également possible d'utiliser la propriété parallelFiles) :
import ch.capi.net.*; var loader1:MassLoader = new MassLoader(); //tous les fichiers en même temps var loader2:MassLoader = new MassLoader(1); //1 fichier par 1 (loader séquentiel) var loader3:MassLoader = new MassLoader(3); //3 fichiers par 3
La classe MassLoader s'occupe de gérer différents événements :
import ch.capi.net.*; import ch.capi.events.*; import flash.events.*; //création des fichiers var factory:LoadableFileFactory = new LoadableFileFactory(); var file1:ILoadableFile = factory.create("data/file1.txt"); var file2:ILoadableFile = factory.create("data/file2.txt"); var file3:ILoadableFile = factory.create("swf/file.swf"); var file4:ILoadableFile = factory.create("xml/file.xml"); //création du loader var loader:MassLoader = new MassLoader(); //ajout des fichiers dans la liste de chargement loader.addFile(file1); loader.addFile(file2); loader.addFile(file3); loader.addFile(file4); //écouteur var l:Function = function(evt:Event):void { trace("event : "+evt.type); } loader.addEventListener(Event.OPEN, l); loader.addEventListener(Event.CLOSE, l); loader.addEventListener(Event.COMPLETE, l); loader.addEventListener(MassLoadEvent.FILE_OPEN, l); loader.addEventListener(MassLoadEvent.FILE_CLOSE, l); //lancement du chargement loader.start();
sortie du code précédent :
event open event fileOpen event fileOpen event fileOpen event fileOpen event fileClose event fileClose event fileClose event fileClose event complete
En admettant que le MassLoader utilisé soit défini comme séquentiel (parallelFiles = 1), la trace ressemblerait à ceci :
event open event fileOpen event fileClose event fileOpen event fileClose event fileOpen event fileClose event fileOpen event fileClose event complete
Je détaille un peu plus ce qui se passe avec la gestion des données bytesLoaded et bytesTotal à l'intérieur du framework.
Lorsque l'on charge plusieurs fichiers d'un coup, on s'intéresse souvent à créer une barre de chargement reflétant l'avancement globale du chargement. Hors cela n'est possible que si l'on connait à l'avance le poid total (bytesTotal) de chacun des fichiers. Comme cela n'est pas possible, le framework a recours à la propriété \\virtualBytesTotal
afin de renvoyer le poids total des fichiers :
import ch.capi.net.*; //création des fichiers var factory:LoadableFileFactory = new LoadableFileFactory(1000); var file1:ILoadableFile = factory.create("data/file1.txt"); var file2:ILoadableFile = factory.create("data/file2.txt"); var file3:ILoadableFile = factory.create("swf/file.swf"); var file4:ILoadableFile = factory.create("xml/file.xml"); //création du loader var loader:MassLoader = new MassLoader(); //ajout des fichiers dans la liste de chargement loader.addFile(file1); loader.addFile(file2); loader.addFile(file3); loader.addFile(file4); trace(loader.bytesTotal); //4000 (chacun des fichiers a 1000 bytes comme valeur pour virtualBytesTotal)
Au fur et à mesure que le MassLoader récupère les données bytesTotal correctes de chacun des fichiers, la valeur bytesTotal de l'objet MassLoader sera ajustée. Le but est que la valeur virtualBytesTotal soit la plus proche possible de la taille réelle du fichier. Si la taille n'est pas fixe (fichier généré via php par exemple), l'idéal est que la valeur soit au-dessus de la taille réel pour éviter que la valeur bytesTotal de l'objet MassLoader soit ajustée vers le haut !
import ch.capi.net.*; import flash.events.*; //création des fichiers var factory:LoadableFileFactory = new LoadableFileFactory(); var file1:ILoadableFile = factory.create("data/file1.txt"); var file2:ILoadableFile = factory.create("data/file2.txt"); var file3:ILoadableFile = factory.create("swf/file.swf"); var file4:ILoadableFile = factory.create("xml/file.xml"); //création du loader var loader:MassLoader = new MassLoader(); //ajout des fichiers dans la liste de chargement loader.addFile(file1); loader.addFile(file2); loader.addFile(file3); loader.addFile(file4); //écouteur var p:Function = function(evt:ProgressEvent):void { trace(evt.bytesLoaded+" / "+evt.bytesTotal); } loader.addEventListener(ProgressEvent.PROGRESS, p); loader.start();
sortie :
0 / 614485 85 / 614485 85 / 409770 170 / 409770 170 / 205055 255 / 205055 255 / 340 340 / 340 340 / 340
Pourquoi la valeur des bytesTotal baisse-t-elle ? En fait, si la valeur bytesTotal de l'objet ILoadableFile est égale à 0, alors le MassLoader va utiliser la valeur virtualBytesTotal pour compenser. Une fois la valeur de bytesTotal connue, c'est elle qui sera utilisée ! Dans l'exemple ci-dessus, la valeur baisse énormément car les fichiers sont très petits.
Cela va-t-il poser problème pour mes barres de preload ? Tout dépend de comment vous l'avez codée… A forciori si vous utilisez un ratio (ou un pourcentage), cela ne posera aucun problème car la valeur bytesLoaded ne sera jamais plus grande que bytesTotal.
La classe MassLoader ne s'occupe pas de savoir si le chargement d'un fichier a été fait correctement ou non. Elle enverra toujours les événements MassLoadEvent.FILE_OPEN lorsqu'elle lance le chargement d'un fichier et MassLoadEvent.FILE_CLOSE lorsque le chargement de celui-ci est fini (avec erreur ou non). Afin de pouvoir gérer finement les erreurs qui pourraient survenir, voila une solution :
var onFileOpen:Function = function(evt:MassLoadEvent):void { var file:ILoadableFile = evt.file; var loadManager:IEventDispatcher = file.loadManagerObject as IEventDispatcher; loadManagerObject.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError); loadManagerObject.addEventListener(IOErrorEvent.IO_ERROR, onIOError); } var onFileClose:Function = function(evt:MassLoadEvent):void { var file:ILoadableFile = evt.file; var loadManager:IEventDispatcher = file.loadManagerObject as IEventDispatcher; loadManagerObject.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError); loadManagerObject.removeEventListener(IOErrorEvent.IO_ERROR, onIOError); } var onIOError:Function = function(evt:IOErrorEvent):void { trace("IOError"); } var onSecurityError:Function = function(evt:SecurityErrorEvent):void { trace("SecurityError"); } var massloader:MassLoader = new MassLoader(); massloader.addEventListener(MassLoadEvent.FILE_OPEN, onFileOpen); massloader.addEventListener(MassLoadEvent.FILE_CLOSE, onFileClose);
Pour les gens s'intéressant un peu à la modélisation de l'API et qui souhaite avoir une vue d'ensemble de l'architecture, ci-dessous un schéma représentatif des liens entre les différents éléments :
Ou sont ces classes ? Elles se trouvent toutes dans le package ch.capi.net !
C'est quoi ces classes internes en grisé ? Je ne les vois pas dans la doc… Et oui ! Ce sont des classes qui implémentent l'interface ILoadableFile. Celles-ci sont masquées car elles seront automatiquement instanciées par la classe LoadableFileFactory suivant ce que vous demander (cf les méthodes de cette classe). En gros, vous n'avez besoin que de savoir que vous récupérer un objet de type ILoadableFile. Plus d'infos par ici !
Tiens par contre je vois une classe MassLoadOrganizer… C'est une classe concernant la gestion de chargement de fichiers inter-dépendants. Cette classe n'est pas encore finalisée (elle va de paire avec la classe OrganizedFile) et fera l'objet d'un prochain tutorial.
Ca veut dire quoi 'aggregates' ? Cela signifie simplement que l'interface IMassLoader (ou plutot ses implémentations) stockera des objets ILoadManager.
Et le 'uses' pour LoadableFileFactory ? La classe LoadableFileFactory s'occupe d'instancier les classes ULoadableFile et LLoadableFile.
Pourquoi la classe AbstractLoadableFile n'implémente pas l'interface ILoadableFile ? Cette classe existe par souci de simplicité envers les implémentations de l'interface ILoadableFile. Ainsi les classes héritant de la classe AbstractLoadableFile qui doivent implémenter cette interface n'auront plus qu'a implémenter les méthodes start() et stop() sans se soucier des propriétés !
Comment installer cette API ? Il faut que le dossier 'ch' soit dans votre classpath (dans les paramètres de publication) ou alors simplement dans le même dossier que le fichier fla.
Est-ce que car marche avec Flash 6/7/8 ? Non. Elle est uniquement compatible Flash 9 / AS3, mais si vous êtes intéresser à la porter en AS2… ![]()
Quelle est la dernière version ? La version 2.0
Est-ce que je peux modifier la valeur de la propriété parallelFiles durant un chargement ? oui, mais cela n'affectera pas le chargement en cours, il faut le stopper, puis le redémarrer.
Si je stoppe le chargement et je le relance, est-ce que tous les fichiers seront rechargés ? non, seuls ceux qui n'ont pas été chargés (ou seulement partiellement) seront relancés.
Après que le MassLoader ait fini de tout charger, la liste des fichiers récupérées par la méthode getFiles est vide… normal ! Lorsqu'un fichier a été chargé, celui-ci est enlevé de la liste de fichiers à charger et l'événement MassLoadEvent.FILE_CLOSE est envoyé. Toutefois les valeurs des propriétés bytesLoaded et bytesTotal sont maintenues à jour.
Je vois plein d'autres classes dans ch.capi.data. Ca sert à quoi ? Ce sont des classes qui servent à la gestion des données. Elles sont en partie utilisée dans l'API MassLoad, mais peuvent sans autre être utilisées dans un contexte totalement différent. Vous trouverez des articles sur ces structures sur le blog d'ekameleon.
Cette API m'intéresse ! Est-ce que je peux l'utiliser dans mes projets personnels/professionnels ? Aucun problème
C'est fait pour ça !
J'aimerais proposer des améliorations/suggestions pour cette API. A qui dois-je m'adresser ? Lancez simplement un sujet sur le forum ou alors contactez-moi. Vos idées/remarques/critiques sont les bienvenues !
L'utilisation de cette API simplifie grandement la gestion de préchargement en masse, et est facilement abordable pour les développeurs moins expérimentés en restant très souple pour ceux qui voudraient approfondir/améliorer son fonctionnement. Le framework offre encore d'autres possibilités qui sont répertoriées dans la documentation.
L'API est téléchargeable ici : Masapi project
Encore des questions? Besoin d'aide? Venez en discuter sur les forums Programmation Dynamique ou Programmation Actionscript.