Introducing the XMLLoader class
I started the XMLLoader class a few months back, but didn’t post right away because it needed a lot of cleanup. Not cleanup in the sense of performance improvement or refactoring—it simply used my old programming style, full of dollar signs and underscores. If anyone remembers seeing my code in that form, I deeply apologize for the pain it must have caused your eyes.
So why an XMLLoader class?—because 99% of the data I load with AS3/AIR is in XML format. I stay far from JSON when possible because in AS3 it’s slower than a slug on its day off. XML is native and uses E4X, which lets developers navigate its information just like you would an AS3 tree. I load XML so often, I found myself copying and pasting the same code each time I’d need it—this is the clearest indicator that a class must be written.
Parsing String data to XML poses an issue that many don’t know about. I only discovered it because of the Twitter API. The API is so janked, it sometimes returns the HTML error page instead of the XML response. It wouldn’t be so bad if it weren’t for an image tag in the HTML that isn’t closed. This immediately throws an “XML parser failure: element is malformed” error. Using try/catch is the only way to avoid this. That’s why I wrote it into XMLLoader. It automatically handles the data, attempts to parse it, and, if there are any errors, the load stops and dispatches an error signal.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package { import com.destroytoday.net.XMLLoader; import flash.display.Sprite; public class Test extends Sprite { public var loader:XMLLoader; public function Test() { loader = new XMLLoader(); loader.retryCount = 2; loader.includeResponseInfo = true; loader.openSignal.add(loaderOpenHandler); loader.completeSignal.add(loaderCompleteHandler); loader.errorSignal.add(loaderErrorHandler); loader.load("http://search.twitter.com/search.atom?q=destroytoday"); } protected function loaderOpenHandler(loader:XMLLoader):void { trace(loader); } protected function loaderCompleteHandler(loader:XMLLoader, data:XML):void { trace(loader, data); } protected function loaderErrorHandler(loader:XMLLoader, error:String, message:String):void { trace(loader, error, message); trace(loader.responseStatus); } } } |
Speaking of signals, XMLLoader is the first class in DestroyFramework to use Robert Penner’s AS3 Signals instead of events. If you have to ask why, you haven’t been programming in AS3 long enough. Each XMLLoader instance has signals for open, complete, error, and cancel. The class also includes a retryCount property that indicates how many times to attempt to load a URL before calling it quits. Since some APIs can be down one second, then up the next, this feature really comes in handy. It’s mainly intended for handling the beloved fail whale.
The last two features include a cancel method and an includeResponseInfo property. Sure, URLLoader has a cancel method, close, but an exception is thrown if you call close when no operation is in progress. This makes sense, but the runtime shouldn’t close down shop when it happens. XMLLoader instead cancels and dispatches the cancel signal when the cancel method is called, and if no operations are in progress, it simply does nothing.
The includeResponseInfo property is an incredibly easy way to tell the loader to return the response status code and headers upon success or fail. Without XMLLoader, you could get this information with an event listener and the appropriate handler, but it’s far easier to flick a switch. In all honesty, I’ve neglected to retrieve this info using URLLoader in the past simply because it’s such a verbose process. Now that it only requires setting the property to true, I know I’ll use it more often than not.
As always, the source code is available on GitHub. Be sure to keep watch—my account will be pretty active these next few weeks.
Nice class, I felt the same way when loading XML, its such a repetitive process, I made an XMLLoader class too. I haven’t posted the latest version of it yet ( ugly style of coding too ), but I really like the retry feature. Good post. I’ve yet to use the new signals system yet, but I see its all the rage.
I (apparently like everyone else) have an XMLLoader class in my internal framework. It uses the dreaded singleton, but you can’t beat its simplicity of use:
XMLLoader.instance.load(”file.xml”, onLoaded);
Which matches all my other loaders:
MediaLoader.instance.load(”file.gif”, onLoaded);
BinaryLoader.instance.load(”file.dat”, onLoaded);
etc.
Yours has some more flexibility and more events/signals though. Different strokes for different folks, I guess.
Thanks for sharing!
@Jackson — I’ve been straying from Singletons more and more lately—I’ve been burned too many times. XMLLoader provides more flexibility because I often need to cancel load operations, handle errors or access more detailed return info.
ps — it’s been a while! I thought you got off the grid :p
@Jonnie – Singletons don’t necessarily get in the way of any of that. Those are actually features that my loaders have. Here’s how I handle canceling:
BinaryLoader.instance.cancel(”file.dat”);
And error handling:
And detailed return info:
Singletons seem to have their pros and cons. your version is still more flexible than mine (pro), but it takes more typing to use (con). Clearly there are more pros and cons and it’s probably a choice best made with the prospective users in mind.
PS: I’m still on the grid. I’ve just been taking a bit of a vacation for the holidays. I’ll be back to writing articles soon!
@Jackson — Since the URL is the identifier in your case, do you have a secondary identifier in case you have multiple load operations of the same URL, but need to cancel a specific one?
@Jonnie – I’ve never run into that situation, but no. As a workaround/hack, you could fake it a bit with a query string:
“file.xml?id=1″
“file.xml?id=2″
My system actually groups the loads. So if you do this:
The second call would not start a load. When the first load finishes, both callbacks will be called. This has been very handy for removing duplicate loads. For example:
That will not start 100 loads. Only one will be started, but the code can remain very simple and the Enemy class does not need to worry about it. There are lots of other ways around this that could have been employed, but they all add some complication. I could have “injected” the “dependency” on the loader and such, made some class to do pooling of loads, or something like that but I didn’t. I went the simple way and it seems to work out for me and the users of my loaders. Still, it has its drawbacks. Perhaps differentiating between loads by keys other than the URL is one of them.
Interesting class…
But something surprises me: you say you use XML because it’s native (so it’s faster than JSON, for example) but you use the Signal logic instead of the EventDispatcher one, which is native.
Are Events that bad?
Using Signals doesn’t slow things down too much?
I’m always interested in new ways of thinking (used to love Grant Skinner’s AS2 GDispatcher, for example) but the first step to using them is to understand why other folks do so!
By the way, thanks for changing you programming style! I guess your previous one didn’t match Adobe’s recommendations, right?
I don’t know the exact terminology for the explanation, so I’ll be as broad as I can. When I say ‘native’, I mean it takes advantage of non-AS3 code underneath the hood. Because of this, the XML class couldn’t stand alone as a single AS3 file. EventDispatcher, on the other hand, is not native in the sense that XML is. It is a single AS3 file. It could be rewritten and improved with just pure AS3.
Signals is faster—from my tests, I saw a ~25% performance boost with dispatching. It lacks the event instantiation overhead that EventDispatcher has—there are no event instances to create each time dispatch is called. Signals use classes as event types instead of strings and provide a serious amount of flexibility with listeners. If I were you, I’d give it a shot. You feel ‘cleaner’ after using Signals
In the past few months, I’ve been following Adobe’s coding conventions pretty closely. I find my code much cleaner and easy to read now.
Thanks, I’ll give Signals a try for sure.
About Adobe’s conventions: I totally agree (and also try to follow them, too)!
First time I’ve seen the signal pattern, it does look like less code.
It breaks the event model that display objects use, not sure I’d like my projects dispatchers use different standards, but I’ll keep an eye on the project.