So far I’ve repurposed List into a DataGrid, Tree, DateField and ColorPicker. Here is my attempt to convert List into Menu and MenuBar. Usual caveats apply. There are probably bugs, the visuals need tuning, and there is no guarantee that the one we finally ship someday will look anything like it.
The DownloadProgressBar is the thing you see while the SWF is being downloaded. It also shows a progress bar while the application is being initialized. The one we ship is pretty vanilla and boring to watch if you have a large SWF that takes a long time to download or initialize.
You can create custom ProgressBars by implements IPreloaderDisplay on a Sprite and doing whatever you want in it. The trick is that you can’t use Flex classes since they haven’t been downloaded or initialized yet, and you can’t probably don’t want to do asynchronous things like load images since you’re already in the process of getting the SWF over the network.
I’ve posted two different prototypes of custom DownloadProgressBars. Remember, these are prototypes so they probably have bugs and were only tested on FIreFox on the Mac but the I just want to give you an idea of things that are possible.
The first one displays a set of “slides” the user can read while the application is starting up. The ones in the demo are ugly, but are there to give you an idea of what capabilities I was able to code up in as little code as possible. You can’t use embedded fonts since that will take too long to download. You probably don’t want to use bitmaps either because of their size. But you can draw filled shapes and add text and format that text to some degree. Yeah, you could try to mimic as much of FXG or SVG as possible, but again, that will probably take too much code.
An interesting feature of the SlideShow preloader is that it gets its slide data from the HTML wrapper. That way the slide data isn’t baked into a SWF and you don’t have to make an additional network call to get the slide data. If you add some server logic, you can generate different slide data in the wrapper based on any other knowledge you have of the user without having to change the SWF.
I haven’t gotten the SlideShow demo to work on Safari. I’m not sure if it is because Safari doesn’t handle XML or because there is something I haven’t configured in Safari. I could re-write the wrapper to use appended strings instead of XML if I really wanted to make sure it worked on Safari.
Run SlideShow Demo
The second one displays a screen capture taken just before the application was last shut down. It leverages the onBeforeUnload in a custom HTML wrapper. As the application shuts down, it saves its state and a screen snapshot in a local SharedObject. On startup, the custom ProgressBar grabs the snapshot and displays it right away so it appears the app is up quickly. In the demo, I purposely blur the snapshot so the user knows not to try to interact, but you don’t have to do that if you don’t want to. Then as the application finishes up, it restores the UI widgets to their last state based on the data stored in the SharedObject and then the application can be used again. You will need to run the demo twice (or refresh the page after making some changes) to see the snapshot in use.
Run SnapShot Demo
Apparently, some folks want the text in a menu bar to change when you rollover or select it to show a menu. I’m not sure why the default menuBarItemRenderer doesn’t respond to textRollOverColor and textSelectionColor, but here are the modifications I made to get it to work. Usual caveats apply.
We also ran out of time to do a Spark-based DateField/DateChooser and ColorPicker. It is on the list for future releases. I was able to hack a DateField and ColorPicker using all Spark components. I have no idea if the version we ship in the future will look anything like this at all and I’m sure there are bugs, but it will give you an idea of the flexibility and consistency in the Spark architecture. The components on the left are the ones I created, the ones on the right are the old mx versions.
Usual caveats apply.
So many folks want to know how to put a checkbox in the DataGrid header and have it do select-all/deselect-all. Here’s how I would do it, including how to make the checkbox appear tri-state when only some things are selected.
Usual caveats apply.
I’ve seen a few questions about how to restrict drag and drop so you can’t just drag something anywhere. I had some spare cycles so I put this together. There are probably several ways to do this, but I chose to delay the mouseMove handler to run after the DragManager has done its work, and reposition the dragImage and cursor in the mouseMove handler.
You should be able to create variants that only allow vertical movement or don’t allow the dragImage outside of a certain rectangle.
Usual caveats apply. And yes, it isn’t a real game and doesn’t handle overlaying tiles.
We’re winding up development of Flex 4.0 and the Spark components. As most of you know, we didn’t have time to make Spark equivalents of the MX components. One missing components is Tree.
The MX Tree is based on a List and has a custom dataProvider that flattens the hierarchical data and uses custom renderers that offset their labels to indicate depth of the nodes. One of the reasons there is no Spark Tree is that we’re not quite sure that is the right design for a Tree. Instead, it might have a custom layout that does the offsetting, or it might be better to know how to walk a hierarchy instead of having it flattened for you. Watch for that discussion after Flex 4.0 ships.
In the interim, Flex 4.0 will have a MXTreeItemRenderer class that allows you to create item renderers for MX Tree using the same workflow as creating item renderers for Spark List.
I had some spare cycles recently and decided to see what it would take to re-create the MX Tree pieces in Spark. I got this far in 2 hours. It can only handle XML and doesn’t have open/close effects but because it is based on Spark List, you can customize this Tree the same way you can customize other Spark components. Customizing MX Tree is difficult at times.
There is no guarantee that a Spark Tree will be anything like this mock up. Usual caveats apply. I’m sure there are bugs and missing features, but I’m posting to give folks ideas if they absolutely have to display hierarchical data and need something that Spark makes easier like custom scrollbars.
One of the goals of Spark is to use composition to create custom components. For Halo, everything was about performance and we hardwired a lot of custom subclasses together to get things to work as fast as possible. All of those custom subclasses made extending components very difficult at times.
One of the axioms of programming is that prototyping is usually much easier than creating something that is production-ready.
Most of you know that Flex 4.0 won’t have a Spark DataGrid. There just wasn’t enough time on the schedule. We created a Spark-based class called MXDataGridItemRenderer that allows you create item renderers for DataGrid using the same basic workflow as creating item renderers for Spark List. But the ways you customize the rest of DataGrid is different than the way you’d customize other Spark components.
I had some time recently to prototype a DataGrid-like thing by compositing two Spark Lists together. It took about a day or so to get this far. You can drag the headers to re-order them, resize the column widths, click on the headers to sort. And since it is composed of Spark pieces you can customize aspects of it like you would other Spark components.
Remember, this is a prototype. Not fully debugged or fully featured, and there is probably performance issues if you have lots of columns. I’m just posting it in case it inspires ideas for how you can create things in Spark that mimic things that exist in the MX components that are missing from Spark. For example, if you wanted to add a footer, it should be much more obvious that you can do that by adding a third List. If you wanted locked rows and columns, you’d probably just add more Lists.
Usual caveats apply. Also, there is absolutely no guarantee that when we finally produce a Spark DataGrid that it will be composed in the same way.
As of this writing, the Flex team does not know of any scenario that pins a module in memory forever assuming the developer has cleaned up all references.
If you have a module that does not unload, the steps to diagnose the problem are:
1) Make sure the module is being loaded into a child applicationDomain (use default parameters for the load() method in most cases)
2) Use the profiler to make sure there are no references to objects in the module. The most common causes of leaks are:
a. Styles. Even without any mx:Styles tag in your MXML, if a module uses a component that is not used by the main application, the module will register the default styles for that component with the StyleManager and pin the first instance of the module. You can use the -compiler.keep-generated-actionscript compiler option to see which default styles the module and main application are using. You can use the -compiler.keep-all-type-selectors compiler option on the main application to force the main application to register the default styles for every component in the defaults.css file. Prior recommendations to load style declarations via runtime CSS (StyleManager.loadStyleDeclarations) are withdrawn. Runtime CSS modules register styles in a different way so they can be unloaded and will not prevent this problem.
b. Resources. If a module uses components that use ResourceBundles that are not used by the main application, those resource bundles will get registered with the ResourceManager and pin the first instance of the module. You can use the -compiler.keep-generated-actionscript compiler option to see which resource bundles the module and main application are using. You can force the main application to have those additional resource bundles by adding the resource bundle metadata to the main application. For example, to add the “controls” resource bundle, you would add to the main application MXML file: [ResourceBundle(“controls”)] If you are loading your resource bundles as modules, make sure the resource modules have completed loading before loading the module that is being pinned.
c. ExternalInterface.addCallback. Modules should not use ExternalInterface.addCallback. That registers the method with the browser and there is no way to unregister it at this time. The recommended practice is to have the main application register a method that will call a function reference and put the module’s method in that function reference, then set the function reference to null when the module unloads.
d. Timers and other timer mechanisms. The use of Timer, setTimeout(), and setInterval() can cause leaks. Timer’s must be stopped and have their listeners removed, setTimeout and setInterval must be paired with calls to clearTimeout() and clearInterval().
e. Listeners to events from objects outside of the module. Use weak references or make sure to remove the event listeners. Remember, when you call a.addEventListener(“foo”, b.someMethod), it is ‘a’ that has a reference to ‘b’, not the other way. If you are listening to a parent or stage or singleton, those objects now have a reference to the object that whose method is going to get called and can cause leaks.
f. Focus. If an object in the module has focus, the FocusManager will still have a reference to the module. A good UI will move focus to some other object outside the module before removing the module.
g. RemoteObject. If a module brings in a data class that is going to be part of a server query, that class can get registered with the Player and result in a reference to the module. Link data classes into the main application when possible.
h. Loaded Images. If a module loads an image, that image must be unloaded otherwise the player will still keep some data buffers around.
3) Once there are no references to objects in the module, the Flash Player can still keep a module in memory for a while if objects in the module ever had focus. Other activity in the application, such as typing and clicking will eventually release references to the module and the module will be freed from memory. Try typing and clicking in the main application or another module and see if the pinned module gets garbage-collected.
4) Debug-versions of a module on the Debugger Player can also lead to a module being stuck in memory. Debug-versions contain debug information that can get registered with the debugger and not released. The final test is always to use release versions of the modules and application on a release version of the player.
A word (or 50) about unloadAndStop(): UnloadAndStop is a new API in player 10. It is intended to stop audio and video playback and the timeline, but does not claim to deference all references to classes in a loaded SWF and has not been proven to help modules free themselves from memory. While it may help child applications loaded via SWFLoader, it is not used by modules. Modules actually call Loader.unload() on themselves immediately upon completion of loading so that they will be available for garbage collection once all references to objects in the module have been removed. This is because a module can be used to make multiple instances of the various classes in the module and we don’t want to add the overhead of tracking all of those instances or require that a developer have some other way of tracking when all references to a module have been removed so they could know when to call unload().
It seems that the most common scenarios involving memory leaks are the ones involving loading and unloading multiple SWFs like modules and sub-applications. Every day, we learn more and more about how the player manages memory and its other idiosyncracies, so it is time for another summary.
When debugging suspected memory leaks when loading/unloading SWFs, I generally do the following:
1) Set up the app or a test harness to load and unload the SWF multiple times (at least 3) and force a garbage collection pass after each unload or unload, then use the profiler to see how many copies of the module’s xxx_FlexModuleFactory or the subapplication’s xxx_SystemManager are in memory. If more than 1, keep loading and unloading and see if that number continues to grow. Any module or SWF that introduces a new component with styles will register those styles with the StyleManager and stick around forever the first time it loads. You can prevent that from happening by pre-loading the styles in the main app or via a CSS module. A second copy might stay around if it was the last thing loaded because the player or FocusManager might still be hanging onto it. If you see more than 2, that’s definitely a leak and you should use the profiler to find the leak.
2) After several loads and unloads, I take a memory snapshot, then do more loads and unloads and take another snapshot. I clear all filters, remove percentages, sort by class name and compare manually the number of instances of every class. They should match exactly, except maybe for a few Strings and sometimes, WeakReference. Everything else is suspect and deserves investigation.
3) Once I think I got all references to the SWF cleaned up, I next run several loads and unloads in the debugger and check the console. I am looking for lines in the debug output that start with:
That tells me that the player thought everything was cleaned up and unloaded the SWF. Note that it may not say that right away, even after GC requests as sometimes the player has internal references to a SWF that get cleaned up “later”. If I don’t see that, I go back to step 2 and compare memory snapshots looking for other things that might be leaking
4) Now that I’m convinced that even the player thinks it is ok to unload the SWF, if System.totalMemory is still increasing, the final test is to export release builds for all swfs and run them in a release player. The debugger player seems to hang onto debug information in the SWFs and can skew System.totalMemory. In recent tests, once I get past step 3, the release player’s reporting of System.totalMemory is much more acceptable, capping at a much smaller and acceptable maximum memory value.
5) Once you get past that, some of you might still see memory attributed to the player still growing when using OS tools to examine the player process. That remains an open area of investigation by the player team. For Internet Explorer, one often finds that minimizing IE causes its claim on memory to shrink, implying that it is something to do with IE’s memory management and not the Flash Player or your application. We don’t know of any way to programatically force IE to give up that memory. We also have seen reports of other browsers reporting memory growth even though Flash thinks things should be unloaded. If you can reproduce that in a small test case, file bugs with those test cases.