Loading Reloaded
Controlling loading is tedious, error prone, and each projects has such specific requirements that you end up rewriting loading code. A lot. In my last project I spent 4 hours debugging download code and promised my self I would solve this annoyance once and for all. So in the best "let me be lazy" fashion, I decided to write my final loading code.
Everybody has been there, and there are many classes around to manage loading, but I've never found one that was complete, flexible and easy enough to use. AS3 has created some kind of loading hell: so many classes to import and understand, a plethora of listeners, the need for error handling and the new API objects (Bitmap, DisplayObject) have added more corner cases to remember. Worse still, videos are still left in some twisted logic maze.
BulkLoader is my shot at trying to unify the most common cases for loading management into a reusable library for AS3.
It is licensed under an open source MIT license. Features:
- Unified interface for different loading types.
- Unified progress notification.
- Events for individual items and as a group.
- Priority.
- Stop,resuming and removing individually as well as in bulk.
- Configurable number of maximum connections.
- Cache managing.
- Statistics about loading (latency, speed, average speed).
- Multiple kinds on progress indication: ratio (items loaded / items to load), bytes, and weighted percentage.
- Multiple number of retries.
- Configurable logging.
- Video: meta data storage, streaming as soon as connection is open, sane progress handling.
- Helpers for different kinds of contents
- Easy memory management (releasing objects)
- Connection pooling.
Design goals:
- Minimal imports.
- Few method to learn.
- Dynamic nature: items can be added by specifying a url as a String or a URLRequest .
- Items can be assigned an identifier key to be used on retrieval.
At a glance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var loader : BulkLoader = new BulkLoader("main");
loader.add("bg.jpg");
loader.add("config.xml");
loader.add("soundtrack.mp3");
loader.add("intro.flv");
loader.addEventListener(BulkLoader.PROGRESS, onProgress);
loader.addEventListener(BulkLoader.COMPLETE, onComplete);
loader.start();
function onProgress(evt : BulkProgressEvent) : void{
trace(evt.percentLoaded);
}
function onComplete(evt : Event) : void{
var bgBitmap = loader.getBitmap("bg.jpg");
addChild(bgBitmap);
var video : Video = new Video();
video.attachNetStream(loader.getNetStream("intro.flv"));
parseConfig(loader.getXML("config.xml"));
}
|
- adds 4 items with different typed to be loaded: xml, mp3, flv, jpg.
- created listeners for progress notification and when all items are done.
I needed a class that:
- Would wrap up errors elegantly: retries and no errors on browser tabs closed.
- Progress that works with many simultaneous items and many simultaneous items of very different sizes
- Would let me use (and let me know) as each item was ready, and would allow items that can be streamed (video, sound) to be consumed as soon as possible.
- Would treat video as any kind of asset.
I am pretty happy with the results. I feel BulkLoader strikes a good balance between ease of use, flexibility and power. At work we've used it in a few projects, and users like it.
The thought of managing multiple loadings by hand again makes me shiver. It's the same feeling I got when I started to use a tweening library.
The project is hosted at github. Right now, I believe it's very close the final API. Mostly bug fixes and other enhancements.
Links:
- project home at github
- the issue tracker: if you find a bug, please let me know and I'll get it fixed asap.
- An online doc.
- The WIKI developer guide.
- The .
Thanks to:
- Igor Almeida: my coworker who has been a thorough beta tester. A couple of months ago he wrote a loading manager class that, while has little resemblance to BulkLoader, provided many insights and a good road map of what works (and what doesn't). Igor reported bugs and gave a lot of feedback on features and ideais.
- Zeh: in long emails Zeh has given valuable ideas and discussed a few key issues. Having Zeh give you design ideas is (to steal a phrase from Paul Graham) like having Mick Jagger playing on your son's Bar Mitzvah.'
getting a third opinion
Hi Folks,
Thanks for the good words.
Doob: I didn't make myself very clear, but you can specify urls as you see fit:
loader.add("level1/bg.jpg"); loader.add("level2/bg.jpg"); loader.add("");Of, course using different id's will make your life easier. You only have to watch out if you have the same urls being used as POST requests, since those will look like the same. Then you can either use different id's or pass a URLRequest object directly and store it so it can be used to fetch the loaded content.
Cheers
any chance u can make the LoadItem.type propery public? or have a method along the lines of loaderInstance.getContentType()...
i.e i have a use case where an asset is loaded from an xml file. I don't know if it's a swf/jpg/png.... seeing as your class is already figuring it out, it would be handy to have access to it without having to loop through a list of file types again... p;)
Thanks for the public contribution!
Just this morning I was converting my queue loader to AS3 and I was thinking how I really need to have it support more media types for my sanity, and now I can move on to the more fun parts of development. Thanks a lot.
Quick question, I assume you can change the loading priority if needed mid-stream? And with that, does AS3 handle stopping a load better than AS2? AS2 would drive me nuts when stopping a load only to have the engine keep loading the .swf. Of course, the engine would not display it, as all references would be removed. Still, it kept eating bandwith.
Folks, I'd ask using the google's for suggestions and feature requests, just so that they don't get lost, but they're dutifully noted.
: This seems like a good idea. Thanks
McBride: As3 is capable of actually closing the connection, thus freeing bandwidth, which is great. Currently changing an item's priority mid flight would reflect on the queue on the next available connection, but wouldn't close a loading operation in place.
This is great.
It would be nice if the LoadingItem stored a ref to the bulkLoader that contained it. This would allow eg. a thumbnail gallery to use myLoadingItem.bulkloader.getBitmap("id") when a LoadingItem it has been tracking is complete without it needing to store a ref to the bulkLoader or get a ref using its static methods.
:
Sure can, the , or the short story:
// get progress for main video only: loader.get("my_video.flv").addEventListener("onProgress", onVideoProgress);
For a variety of reasons (mostly memory management) I work hard not to expose LoadingItem instances too much. The way to get a handle of a BulkLoader instance is:
var loader : BulkLoader = new BulkLoader("main-site"); // later anywhere on your code you can fetch // a reference to this BulkLoader instance // using the static function BulkLoader,getLoader: var loader : BulkLoader = BulkLoader.getLoader("main-site");
I was having a heck of a time trying to track several items in a loader using a single onProgress function. In that function, I could not get the evt.target.id to show the proper id of the current item, it was always null. Finally figured it out: within the LoadingItem.as file, I changed "internal var id : String;" to "public var id : String;" and it now reports the id correctly in the function. Wee!
@ cksachdev Yup there is, just pass a LoaderContext to the props parameter, such as:
loader.add("some.swf", {context:myLoaderContext}
: This is fixed in the newest revision, now the id property of LoadingItems is public.
: This is a great suggestion, I just to think the interface throughly, stay tuned.
Hey great job with this class, very usefull!
I was wondering if you have any sample files showing some basic usage?... In your download zip the examples folder there is nothing. (I was having a couple of issues with an error while trying your code post above so I was hoping to see a working example to see where i'm going wrong)
Hey Arthur, great work!
I want to use this package, but it throws an Exception.
German Error Message: TypeError: Error #1034: Typumwandlung fehlgeschlagen: flash.events::IOErrorEvent kann nicht in br.com.stimuli.loading.BulkErrorEvent umgewandelt werden. at flash.events::EventDispatcher/flash.events:EventDispatcher::dispatchEventFunction() at flash.events::EventDispatcher/dispatchEvent() at
flash.net::URLLoader/flash.net:URLLoader::redirectEvent()
It says that its not able to convert the Event-Object to an BulkErrrorEvent-Object.
Do you have an idea on this?
Hi Arthur Debert, firstly i'd like congratulate you by the excellent work realized in gringo and by this project now. I tried run a example with BulkLoader and i not had success. I get the error message with BulkProgressEvent, look.
1046: Type was not found or was not a compile-time constant: BulkProgressEvent.
The source was created with base in the documentation from svn.
Hi, I am using BulkLoader but have a problem in IE. I am loading an image like so...
_bulkLoader.add(url); bulkLoader.get(url).addEventListener(Event.COMPLETE, onImageLoaded); bulkLoader.start();
the complete event fires, but the bitmap object is always null for the first few tries:(
works fine in fire fox
cheers
MaTT
Hi Matt.
I've been using BulkLoader on I.E. without any issues, so I think I need a bit more information on this:
-
- Which version of i.e ?
-
- Can you provide a sample of the code you are working with?
As a general note, this is not be best place for bug reports or questions.
I've setup a exactly for that , so it's easier to follow. I'd ask to post questions over there.
Thanks Arthur
Hi Matt, Thanks for this great class, I am running in a small dilema and that has to do with assigning the same loaded file to different elements, for example
var tire1Bitmap = loader.getBitmap("tire.jpg"); var tire2Bitmap = loader.getBitmap("tire.jpg"); var tire3Bitmap = loader.getBitmap("tire.jpg"); var tire4Bitmap = loader.getBitmap("tire.jpg");
addChild(tire1); addChild(tire2); addChild(tire3); addChild(tire4);
only "tire4" will display the image... I know i could load the same item 4 times or maybe I need to create an actual movieclip in the library add the element to that library element and then duplicate it....
Any other shortcuts or ideas?
Helmut,
You need to make copies of the Bitmap in order to add them to different DisplayObjectContainers. Here's how you do that.
var tireMainBitmap = loader.getBitmap('tire.jpg'); var tire1Bitmap = new Bitmap(tireMainBitmap.bitmapData.clone()); var tire2Bitmap = new Bitmap(tireMainBitmap.bitmapData.clone());
:
Sure you can, with only one caveat: if you add items to a bulk loader instance before it's finished loading, your are golden. Else, if you add an item after the bulk loader is done loading all of it's assets, you must call
<bulkLoader>.startagain, because it was stopped when all items had finished loading.
May I just mention one thing about the preloader ...
I've just started playing around with it, and think it might be of use pointing this out (unless it's already been done), but since it took me a while to figure out, I though I'd share it
the drawback of using an id like this is
loader.add("http://www.emptywhite.com/bulkloader-assets/shoes.jpg", {id:"bg"});
if you load the same file twice with two different id;s you won;t get the item back the second time but a null
this of course is rare, as why should you need to load the same item twice, but it's something i did as a test (and might for finished product still) and took me 15 min to figure out.
if you switch to NOT usind ids it's fine
I think that when you are loading the same item twice, it doesn't actually load it the second time, but retrieve t from cache right ? or ama I wrong on this?
anyhow - that's my $0.02
and it's a great and painless way to preload
great work, awesome class. we use it along with our framework and swfaddress and all works great!
q: is it possible to access/display the id of and item during the loadingProgress like:
private function onProgress(event:BalkProgressEvent):void { trace(event.id); }
best michael
would bulk loader supports background loading in the future?
Scenario could be: - a bunch of MUST loaded assets that bulkloader loads and notifies for initial setup - a bunch of POTENTIAL assets that can be cached as background loading without user's notice a. if user happens to activate such asset, bulkloader notifies its already backgroundly loaded. b. or bulkloader initiates normal loading routine for asset that happen to be not yet loaded.
Hi,
Nice work, I'm using it on a project which has a lot of gallery.
Would it be possible to add a functionality?
instead of add an item and create a line for each item or use a loop:
loader = new BulkLoader(nameGallery); for (var i:uint = 0; i < myArrayOfFiles.length; i++) { loader.add(myArrayOfFiles[i]); } loader.start();
Could you parse the type or add another public method? To get:
loader.add(myArrayOfFiles); or loader.addItems(myArrayOfFiles);
Maybe it is already possible?
Romuald
Folks, as stated above, this thread is way to long, and not a very good place for discussions. If possible, please direct your queries to the .
Thanks
:
Sure, just use a evt.target.id
:
It already does. It just requires some attention on you part to whether you'd like to manage two queues or actually one huge queue with the priorities being shuffled.
:
This seems way to spefic, and will only save you one line of code. ;-)
:
Sure is, just watch out for using the same ApplicationDomain (usuning the same Context instance), the mailing list has a few posts about this.
Hey, found a great workaround to my previous post.
public function load_object(path, num, complete_function) { var object_id:String = "LoadObject_"+num.split("_")[0]+"_"+num.split("_")[1]; if (loader.get(object_id)!= null){ spawn_image_container(loader.get(object_id), object_id.toString().split("_")); }else{ loader.add(path, {priority:10,id:object_id}); loader.get(object_id).addEventListener(BulkLoader.COMPLETE, complete_function); } }
Hey All, I read through most of the comments but could not find anything regarding the BulkLoader.ERROR event. Specifically, I want to initiate some functions when Bulk-Loader completes loading an ID but should an ID already be loaded, nothing seems to pass. I noticed in (http://bulk-loader.googlecode.com/svn/trunk/src/br/com/stimuli/loading/BulkLoader.as) that it returns "item" but could not access the item in my own work. How do I pass a function when an ID has already been loaded? Thanks, Matt
Hey All, I read through most of the comments but could not find anything regarding the BulkLoader.ERROR event. Specifically, I want to initiate some functions when Bulk-Loader completes loading an ID but should an ID already be loaded, nothing seems to pass. I noticed in (http://bulk-loader.googlecode.com/svn/trunk/src/br/com/stimuli/loading/BulkLoader.as) that it returns "item" but could not access the item in my own work. How do I pass a function when an ID has already been loaded? Thanks, Matt
Hi i just found that the loader will fail on complete when i try to load an image that is generated by a script. Due to some limitations of the CMS i use i had to call a file called imghandler.HTML and the loader failed. I checked the headers with my proxy and they where fine. Now that i've managed to call the image through a script called imagehandler.JPG everything works fine although the output and headers are exactly the same.
Hello,
If I have, say, 5 items in BulkLoader and my last item fails to load, how do I know that it was the last item in the queue that failed to load? If one or more items fail to load, then COMPLETE event never gets fired and I don't know when BulkLoader has finished trying loading all items in the queue. Am I correct, or am I missing something here? Thanks.
I truly appreciate your work! Thank You!
I am learning your package, and realized that in order for me to implement the BulkLoader.as, I would need to restructure my code. I don't fully understand the intricacies of the Object Tree, but I had an idea that you might be able to add a MovieClip or Sprite instance in the BulkLoader.add(url, mc), with the data loaded from the URL placed in the mc.
Just an idea.
thanks for your contributions.
I'm using the BulkLoader class to import multiple xmls . when I use the--
var thingy : XML = bulkLoader.getXML("file.xml");
I expected the thingy to then be an XMLObject, that could be used to access and navigate through as one would any XMLObject.
However, I'm unable to trace with any of the XMLObject methods.
example (children, child, etc...). This of course doesn't make any since.
any insight would be appreciated...Thanks
Hi,
I'm trying to get progress event on multiple loading of FLVs.(30 of them!) I'm using the weight property but still only getting 1 progress event firing . What could I be doing wrong??
var loader:BulkLoader = new BulkLoader("3dMgtPreloader");
loader.add(MovieClip(parent).MyMigraine_Intro_mc.source, {weight:812, type:"video"}); loader.add(MovieClip(parent).MyMigraine_Main_mc.source, {weight:2209,type:"video", pausedAtStart:true}); //MyMigraine_Pan_mc.source, {type:"video", pausedAtStart:true}); loader.add(MovieClip(parent).MyMigraine_Replay_mc.source, {weight:338,type:"video", pausedAtStart:true}); loader.add(MovieClip(parent).MyMigraine_Help_mc.source, {weight:3175,type:"video", pausedAtStart:true}); loader.add(MovieClip(parent).MyMigraine_FlyUpA_mc.source, {weight:552,type:"video", pausedAtStart:true}); loader.add(MovieClip(parent).MyMigraine_FlyUpB_mc.source, {weight:600,type:"video", pausedAtStart:true});
loader.addEventListener(BulkLoader.PROGRESS, onAllProgress); //loader.addEventListener(BulkLoader.COMPLETE, onComplete);
loader.start()
function onAllProgress(e:ProgressEvent){
var perc1:Number = Math.floor(e.bytesLoaded * 100 / e.bytesTotal);
var perc:Number = loader.itemsLoaded / loader.itemsTotal;
}
Brilliant brilliant class. Takes the preloader pain away. Thanks Arthur.
For anyone else struggling with the infamous null Object errors when loading swfs via Bulkloader, here's something basic you could check for.
Any stage references in the controller class of the swf being loaded might throw the whole thing off. In my stupid case, it was a stage.stageHeight access that broke the whole damn thing.
The best I could think of was pulled out the constructor code and pushed into another function (initClass). And then well..
public function controller():void { addEventListener(Event.ADDED_TO_STAGE. initClass); }
That fixed it, for now. So, just in case anyone else is stuck with something similar, there. That helped me.
Hi,
This is great, however I'm struggling to "refresh" a swf loaded with bulkloader. I successfully load several game swf's and when the user is done with one they return to main menu. However when they return to that game it is in it's completed state, and I'm trying to reset it. Using removeChild doesn't do it, am I looking at this the wrong way? many thanks
Definitely, this class is very helpful to those looking for a very effective loading system. We are developing a new project and I definitely think that this would be a great tool specifically for game. Looking forward to work my way using your loader class. Thanks for this information.
Have the last word