Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Les bibliothèques dynamiques

Compatible ActionScript 2. Cliquer pour en savoir plus sur les compatibilités.

Introduction

Vous l'avez sans doute remarqué, le code AS2 prend une place assez conséquente au chargement. Quelques classes et vous êtes rapidement sur des 30-40Ko au chargement.

Maintenant imaginez que vous avez plusieurs SWF qui utilisent des packages complexes (par exemple vous utilisez AS2Lib, XPath etc). Vous multipliez cette surcharge par autant de SWFs utilisés.

Il est possible de faire un préload sur un swf principal en exportant toutes les classes sur la deuxieme frame et en calculant le preload sur la premiere, mais ce n'est pas l'approche la plus propre.

Principe

Je vais lancer pas mal de trucs théoriques, ça pourrait être déroutant pour certains, et ce n'est pas fondamental pour savoir utiliser ces méthodes. La majorité d'entre vous devrait aller directement à la prochaine section.

Que se passe-t-il lorsque j'execute la commande suivante ?

import com.monsite.monappli.MaClasse;
var instance:MaClasse = new MaClasse();

Et bien le compilateur va interpreter cela de la façon suivante :

if(!_global.MaClasse) {
   _global.MaClasse = new Function(){
        code du constructeur
   };
   var prot = MaClasse.prototype;
   ...
}
var instance:MaClasse = new MaClasse();

Qu'est-ce qu'il faut en retenir ?

  1. le code est interprété en AS1
  2. les classes sont créées en _global, donc accessibles de partout

Partant de cette constatation, avec un peu de jugeotte on se dit qu'un SWF chargé va créer ses classes dans _global de la même façon, et donc qu'elles seront fonctionnellement toutes accessibles après son chargement. C'est la dessus que repose le principe des bibliothèques de fonctions partagées.

En pratique

Nos acteurs

Nous allons travailler avec deux classes toutes bêtes à titre d'exemple :

  • Class1
  • org.aggelos.dll.Class2
class Class1 {
 
	function Class1() {
		trace("constructeur 1");
	}
 
	function maFonction() {
		trace("maFonction 1");
	}
}
class org.aggelos.dll.Class2 {
 
	function Class2() {
		trace("constructeur 2");
	}
 
	function maFonction() {
		trace("maFonction 2");
	}
 
}

Création de la librairie

L'idée est de faire un SWF dont le seul interêt est de forcer le chargement des classes. L'instruction

com.unsite.uneappli.UneClasse;

force l'inclusion de cette classe dans le script.

Attention ! notez qu'il n'y a pas le import et c'est chose voulue ! Lorsqu'on utilise la directive import, si la classe n'est jamais utilisée, elle n'est pas compilée !

Une bonne pratique pour charger toutes ses classes est de créer une classe ClassLoader comme suit :

class ClassLoader extends MovieClip {
 
	public function ClassLoader() {
	}
 
	public function doNothing() {
		Class1;
		org.aggelos.dll.Class2;
	}
 
}

Il suffit donc ensuite de créer un fla, et sur la premiere image simplement mettre

(new ClassLoader()).doNothing();

Il suffit de compiler le swf en l'appelant malib.swf et on obtient de la sorte une librairie chargeable.

Chargement des librairies

Vous l'avez sans doute compris, une librairie se charge de la même façon que n'importe quel SWF. Toutefois, vous pouvez vous faire aider par des petites classes sympas comme le DLLLoader d'Aral BALKAN, qui se décompose en deux classes :

class com.ariaware.arp.utils.dll.DLLEvent 
{
	var type:String;
	var target:Object;
 
	var bytesLoaded:Number;
	var bytesTotal:Number;
 
	function DLLEvent ( type, bytesLoaded, bytesTotal )
	{
		this.type = type;
		this.bytesLoaded = bytesLoaded;
		this.bytesTotal = bytesTotal;
	}
}
import mx.events.EventDispatcher;
import com.ariaware.arp.utils.dll.DLLEvent;
 
class com.ariaware.arp.utils.dll.DLLLoader extends MovieClip
{
	// On Stage
	var deadPreview:TextField; 
 
	// Dynamically created clips
	var loaderShell:MovieClip = null;
 
	//
	// Group: Events broadcast
	//
	// Event: progress - On load progress (every frame)
	// Event: complete - When DLL has completely loaded
	// 
 
	public function DLLLoader ()
	{
		EventDispatcher.initialize ( this );
	}
 
	////////////////////////////////////////////////////////////////////////////
	//
	// Method: loadDll()
	//
	// Starts loading the specified DLL (SWF) file. A valid DLL file is a 
	// SWF file that contains AS2 classes.
	//
	////////////////////////////////////////////////////////////////////////////	
	public function loadDll ( dll:String )
	{
		// Create shell movie clip to load DLL into if it doesn't already exist
		if ( loaderShell == null )
		{
			loaderShell = createEmptyMovieClip ( "loaderShell", getNextHighestDepth());
		}
 
		// Load the movie
		loaderShell.loadMovie ( dll );
 
		// Start the preloader
		onEnterFrame = preloader;
	}
 
	////////////////////////////////////////////////////////////////////////////
	//
	// Group: Private methods
	//
	////////////////////////////////////////////////////////////////////////////
 
	private function onLoad ()
	{
		deadPreview._visible = false;
	}
 
	// Preloader method called every frame after DLL starts loading
	private function preloader ()
	{
		var bytesLoaded:Number = getBytesLoaded();
		var bytesTotal:Number = getBytesTotal();
		if ( bytesLoaded == bytesTotal && bytesLoaded > 10 )
		{
			// Ok, DLL has loaded -- wait a frame for it
			// to initialize so the classes become available.
			onEnterFrame = waitForInit;
		}
		else
		{
			dispatchEvent ( new DLLEvent ( "progress", bytesLoaded, bytesTotal ) );
		}
	}
 
	// DLL has initialized. Broadcast the complete event
	private function waitForInit ()
	{
 
		onEnterFrame = null;
		dispatchEvent ( new DLLEvent ( "complete", getBytesLoaded(), getBytesTotal() ) );		
	}
 
	//
	// Note: Methods to be mixed in by the EventDispatcher
	//
	function addEventListener(){};
	function removeEventListener(){};
	function dispatchEvent(){};
}

L'utilisation en est relativement simple (je vous la fais en simple, sans les Delegate):

import com.ariaware.arp.utils.dll.DLLLoader;
import com.ariaware.arp.utils.dll.DLLEvent;
 
var listener:Object = new Object();
listener.progress = function(evt) {
   //ici on a evt.bytesLoaded et evt.bytesTotal, impec pour un preload  
}
 
listener.complete = function(evt) {
   //chargement fini, librairie initialisée  
}
 
var loader:DLLLoader = new DLLLoader();
loader.addEventListener("progress",listener);
loader.addEventListener("complete",listener);

Voila le principe, maintenant a vous de mettre des routines de chargement d'affilée etc.

Le typage

Voila l'aspect le plus complexe :) Probleme : je suis dans un swf dont je sais qu'il utilisera mes libraires et je veux

  • typer
  • mais ne pas charger mes fonctions

oui mais… les deux sont censément incompatibles vu que le typage implique le chargement ! Il y a deux remedes pour palier à ceci

Les fichiers d'exclusion

Il est possible de préciser des fichiers permettant d'exclure de la compilation certaines classes et uniquement ces classes là. Si elles utilisent à leur tour ces autres classes, ces classes ci seront compilées sauf instruction contraire.

Pour le compilateur flash

On utilise ce qu'on appelle un xml d'exclusion. Si votre fla s'appelle monFla.fla, il suffit de créer le fichier monFla_exclude.xml qui a la forme suivante :

<excludeAssets>
	<asset name="Class1" />
	<asset name="org.aggelos.dll.Class2" />
</excludeAssets>

Ce fichier sera utilisé automatiquement !

Pour MTasc

Il suffit pour MTasc d'utiliser un fichier dont le contenu est une classe completement qualifiée par ligne, donc de la forme

Class1
org.aggelos.dll.Class2

et de taper simplement

mtasc vosfichiers.as... -exclude leFichierDexclusion

Les classes intrinseques

Vous etes vous déjà demandé à quoi servaient les classes déclarées en tant que

intrinsic class MaClass {
}

?

Ce sont simplement des équivalent de fichiers “header” qui servent uniquement au typage pour le compilateur. Donc si au lieu de donner à votre fla un chemin qui pointe vers vos vraies classes vous lui donnez un chemin qui pointe vers des classes intrinseques equivalentes vous avez le typage sans la compilation. Dans notre cas de telles classes seraient ainsi :

intrinsic class Class1 {
 
	function maFonction();
}
 
intrinsic class org.aggelos.dll.Class2 {
 
	function maFonction();
 
}

Attention aux dépendances toutefois ! Sinon vous aurez des conflits de typage :)

Au Secours ! Des outils !

Vous avez affaire à un problème plus complexe ? Comme par exemple un monstrueux paquet de dépendances par exemple parceque vous utilisez as2lib, xpath, remoting ? Merci OSFlash, qui héberge deux outils pour vous aider

Sexie

Bon, c'est mon petit cheri, forcément je l'ai crée de mes mains :p Sexie peut vous concevoir tout ce dont vous avez besoin : fichiers d'exclusion avec toutes les dépendances, et packages complets en intrinseque !

Comment l'utiliser ? Vous vous souvenez de notre classe ClassLoader ? Vous vous demandiez pourquoi elle heritait de MovieClip ? Simplement parceque nous allons en faire un composant :)

  1. ouvrez un fla vierge
  2. créez un clip vide dans la bibliotheque et cliquez sur le bouton droit
  3. dans le linkage liez le a ClassLoader
  4. dans definition de composant idem
  5. choisissez “exporter le fichier SWC”
  6. enregistrez votre compo

Alors oui, vous disposez de la possibilité de distribuer votre bibliotheque en tant que composant maintenant, ce qui est déjà pas mal. Mais maintenant ouvrez Sexie (ce qui necessitera une machine java 1.5). Dans l'invite du haut, allez chercher votre fichier swc et choisissez ce que vous voulez exporter. Tadaaaa, vous avez tout !

Sexie est dispo sur OSFlash

AsiGen

Asigen est un outil crée par Steve WEBSTER qui génère un package intrinseque à partir d'un package original. Ses atouts sont : * extreme facilité d'emploi * il ne regénère que les fichiers qui ont été modifiés * toutefois il génère tout le package, pas seulement les fichiers nécessaires.

Il est disponible lui aussi sur OSFlash