Flex 4 AIR APP unloading module the proper way - apache-flex

I've been looking all over google for this but I cannot find a good answer.
I am using flex 4 and building an air app using modules (there will be plenty of modules because this is a big project).
I managed to load modules in a titlewindow that is called by popupmanager but when the titlewindow is closed the module is not unloaded (garbaged) -- I checked this using the profiler in flasbuilder.
Here is my code, I need to know if I am going in the right direction using modules before getting to much in the project.
Thanks to all
Main APP: MXML
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:tblusersservice="services.tblusersservice.*"
xmlns:valueObjects="valueObjects.*"
xmlns:tbluserservice="services.tbluserservice.*"
width="100%" height="100%" applicationComplete="checkForUpdate()" preinitialize="nativeWindow.maximize();" currentState="login">
<fx:Script source="includes/_loadtracker.as"/>
<s:Panel id="panelmain" includeIn="mainmenu" left="5" width="100%" height="100%" resizeEffect="Resize" title="Main menu">
<s:Image id="companymenu" right="15" top="130" width="118" height="93" buttonMode="true"
click="loadmodule('mod_company', 'Company Information', 931, 446);" source="assets/company.png" useHandCursor="true"/>
</s:Panel>
</s:WindowedApplication>
_loadtracker.as:
// ActionScript file
import flash.filesystem.*;
import flash.events.ErrorEvent;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.display.*;
import air.update.ApplicationUpdaterUI;
import air.update.events.UpdateEvent;
import mx.controls.Alert;
import mx.managers.PopUpManager;
import mx.rpc.events.ResultEvent;
import spark.components.TitleWindow;
import valueObjects.*;
import flash.desktop.NativeApplication;
// Open the pop-up window.
private function loadmodule(modname:String, modtitle:String, modwidth:int, modheight:int):void {
// Create a non-modal TitleWindow container.
settings.moduletoload = modname;
var titleWindow:TitleWindow=
PopUpManager.createPopUp(this, showmodules, true) as TitleWindow;
titleWindow.title = modtitle;
titleWindow.width = modwidth;
titleWindow.height = modheight + 35;
PopUpManager.centerPopUp(titleWindow);
}
showmodules.mxml
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="400" creationComplete="initModule()" close="handleCloseEvent()">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.core.IVisualElement;
import mx.events.ModuleEvent;
import mx.managers.PopUpManager;
import mx.modules.IModuleInfo;
import mx.modules.ModuleManager;
import mx.rpc.events.ResultEvent;
import services.tbluserservice.*;
public var info:IModuleInfo;
public var modclosed:Boolean = false;
private function initModule():void {
this.addEventListener("foobar", handleCloseEventmodule);
info = ModuleManager.getModule("/modules/"+settings.moduletoload+".swf");
info.addEventListener(ModuleEvent.READY, modEventHandler);
info.load(null, null, null, moduleFactory);
}
/* Add an instance of the module's class to the display list. */
private function modEventHandler(e:ModuleEvent):void {
this.addElement(info.factory.create() as IVisualElement);
}
// Handle the close button and Cancel button.
public function handleCloseEvent():void {
PopUpManager.removePopUp(this);
info.unload();
info.release();
info = null;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:TitleWindow>
mod_company.mxml
<s:Module xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:tblcompanyservice="services.tblcompanyservice.*"
xmlns:valueObjects="valueObjects.*"
width="931" height="446"
creationComplete="LoadData()">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function dataGrid_creationCompleteHandler(event:FlexEvent):void
{
getAllTblcompanyResult.token = tblcompanyService.getAllTblcompany();
}
]]>
</fx:Script>
<fx:Script source="../includes/_company.as"/>
<fx:Declarations>
<tblcompanyservice:TblcompanyService id="tblcompanyService"
fault="Alert.show(event.fault.faultString + '\n' + event.fault.faultDetail)"
showBusyCursor="true"/>
<s:CallResponder id="getTblcompanyByIDResult" fault="Alert.show(event.fault.faultString + '\n' + event.fault.faultDetail)"
result="tblcompany = getTblcompanyByIDResult.lastResult as Tblcompany"/>
<valueObjects:Tblcompany id="tblcompany"/>
<s:CallResponder id="updateTblcompanyResult"/>
<s:CallResponder id="getAllTblcompanyResult"/>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Label x="81" y="41" text="COMPANY NAME"/>
<s:Label x="81" y="71" text="ADDRESS"/>
<s:Label x="83" y="131" text="CITY"/>
<s:Label x="83" y="161" text="STATE"/>
<s:Label x="83" y="191" text="ZIP"/>
<s:Label x="83" y="221" text="COUNTRY"/>
<s:Label x="582" y="41" text="TELEPHONE"/>
<s:Label x="582" y="71" text="FAX"/>
<s:Label x="582" y="102" text="WATTS"/>
<s:Label x="83" y="276" text="OWNER"/>
<s:Label x="83" y="324" text="LOGO PATH"/>
<s:TextInput id="fNameTextInput" x="185" y="32" width="323" text="{tblcompany.fName}"/>
<s:TextInput id="faddressTextInput" x="185" y="62" width="256" text="{tblcompany.faddress}"/>
<s:TextInput id="faddress2TextInput" x="185" y="92" width="256" text="{tblcompany.faddress2}"/>
<s:TextInput id="fcityTextInput" x="185" y="122" width="256" text="{tblcompany.fcity}" textAlign="left"/>
<s:TextInput id="fstateTextInput" x="185" y="152" width="256" text="{tblcompany.fstate}"/>
<s:TextInput id="fzipTextInput" x="185" y="182" width="81" text="{tblcompany.fzip}"/>
<s:TextInput id="fcountryTextInput" x="185" y="212" width="256" text="{tblcompany.fcountry}"/>
<s:TextInput id="ftelTextInput" x="701" y="32" text="{tblcompany.ftel}"/>
<s:TextInput id="ffaxTextInput" x="701" y="62" text="{tblcompany.ffax}"/>
<s:TextInput id="fwattsTextInput" x="701" y="92" text="{tblcompany.fwatts}"/>
<s:TextInput id="fownerTextInput" x="185" y="266" width="418" text="{tblcompany.fowner}"/>
<s:TextInput id="flogopathTextInput" x="185" y="314" width="644" text="{tblcompany.flogopath}"/>
<s:TextInput id="fidTextInput" x="224" y="379" text="{tblcompany.fid}" visible="false"/>
<s:Button id="button" x="79" y="379" label="Save" click="button_clickHandler(event)"/>
<s:DataGrid id="dataGrid" x="158" y="242"
creationComplete="dataGrid_creationCompleteHandler(event)" requestedRowCount="4">
<s:columns>
<s:ArrayList>
<s:GridColumn dataField="fid" headerText="fid"></s:GridColumn>
<s:GridColumn dataField="fName" headerText="fName"></s:GridColumn>
<s:GridColumn dataField="fowner" headerText="fowner"></s:GridColumn>
<s:GridColumn dataField="faddress" headerText="faddress"></s:GridColumn>
<s:GridColumn dataField="faddress2" headerText="faddress2"></s:GridColumn>
<s:GridColumn dataField="fcity" headerText="fcity"></s:GridColumn>
<s:GridColumn dataField="fzip" headerText="fzip"></s:GridColumn>
<s:GridColumn dataField="fstate" headerText="fstate"></s:GridColumn>
<s:GridColumn dataField="fcountry" headerText="fcountry"></s:GridColumn>
<s:GridColumn dataField="ftel" headerText="ftel"></s:GridColumn>
<s:GridColumn dataField="ffax" headerText="ffax"></s:GridColumn>
<s:GridColumn dataField="fwatts" headerText="fwatts"></s:GridColumn>
<s:GridColumn dataField="flogopath" headerText="flogopath"></s:GridColumn>
<s:GridColumn dataField="femail" headerText="femail"></s:GridColumn>
</s:ArrayList>
</s:columns>
<s:typicalItem>
<fx:Object faddress="faddress1" faddress2="faddress21" fcity="fcity1"
fcountry="fcountry1" femail="femail1" ffax="ffax1" fid="fid1"
flogopath="flogopath1" fName="fName1" fowner="fowner1" fstate="fstate1"
ftel="ftel1" fwatts="fwatts1" fzip="fzip1"></fx:Object>
</s:typicalItem>
<s:AsyncListView list="{getAllTblcompanyResult.lastResult}"/>
</s:DataGrid>
</s:Module>
_company.as
// ActionScript file
//import flash.desktop.NativeApplication;
import flash.events.MouseEvent;
import flash.events.Event;
import mx.controls.Alert;
//import mx.core.Application;
//import mx.core.mx_internal;
import services.tblcompanyservice.*;
import valueObjects.*;
protected function LoadData():void {
getTblcompanyByIDResult.token = tblcompanyService.getTblcompanyByID(parseInt("1"));
}
protected function button_clickHandler(event:MouseEvent):void {
tblcompany.fid = parseInt(fidTextInput.text);
tblcompany.fName = fNameTextInput.text;
tblcompany.fowner = fownerTextInput.text;
tblcompany.faddress = faddressTextInput.text;
tblcompany.faddress2 = faddress2TextInput.text;
tblcompany.fcity = fcityTextInput.text;
tblcompany.fzip = fzipTextInput.text;
tblcompany.fstate = fstateTextInput.text;
tblcompany.fcountry = fcountryTextInput.text;
tblcompany.ftel = ftelTextInput.text;
tblcompany.ffax = ffaxTextInput.text;
tblcompany.fwatts = fwattsTextInput.text;
tblcompany.flogopath = flogopathTextInput.text;
tblcompany.femail = "";
updateTblcompanyResult.token = tblcompanyService.updateTblcompany(tblcompany);
//Alert.show("Modifications saved");
//this.dispatchEvent(new Event("foobar", true));
}

Unloading Flex modules has historically been problematic. Flex 4 and I assume 4.5 started to chip away at the underlying issues to make our lives easier.
When something in your main app maintains a reference to an object in your module, it will prevent your module from unloading. There are many ways this can happen. Flex may still cause some of these problems due to the way it works. But the major headaches have been mitigated (I'd make sure you're on Flex 4.5 though).
You're definitely wise for thinking about this now. Not the choice about whether to use modules, but making sure they are unloading.
The article I linked to is an old post from a Flex SDK developer, while some of those issues may no longer exist the concepts should be enlightening.
Edit
On further review, only one of the things that jumped out at me seem worth looking at:
The script tag in your module may have the same problems that styles can introduce (also could be a red herring). The idea is that the first class that uses this script may be permanently associated w/the script internally by Flex. If that class is a module, it will never unload. As a test, you can try declaring the script tag in the WindowedApplication class as well (even though it's not being used).
The other thing I thought about was bindable variables in your view. But on further review, I can't see how those could be causing a memory leak.

I think the problem is just that you're creating a new TitleWindow each time, and the TitleWindow adds an event listener to the module that is loaded through the module loader and never removes it. In theory, PopupManager.removePopup should also dereference your TitleWindow each time, but, honestly, someone who would write this functionality as a global/static Class probably can't be trusted to adhere to good practice in other places, so you should probably use F3 to look through the code on PopupManager and make sure it's removing any event listeners it added and nulling the reference to your title window. Since it's a static Class, once something talks to it, if it doesn't dereference things properly they will stick around for the life of your app.
Next, you're manually creating an instance of your Module even though you already have one that's loaded. You do a few things (short of removing your event listener) to try to let go of the loader, but you never release the manually created instance. To simplify things a bit, try just adding the loaded module instead of creating an extra one.
Another suggestion is to not use View elements in lieu of a data store. So, instead of binding to the VO, when you get it populate your fields from it. When anything changes, update the VO based on the VO's value. This results in a bit more code, but for one thing, you always know your data is up to date. For another, the data can travel if it needs to, independently of the View. But this is one thing that could help you eliminate the possibility that bindings are holding the VO in memory.
There are also a few things you do that create sort of a faint barnyard smell around your code, and these could also be sources of the problem. For instance, your title window subclass seems to have a reference to settings, even though you haven't given it one. This suggests settings is actually a static Class, and that you've ignored the naming convention that Classes should start with an upper case. More importantly, if you have communication going on in the back channel like this, there's no way to look at your code and determine that you don't have something going on where somehow you've given settings a reference to something that it is holding onto.
You also have what appears to be an undeclared variable, modulefactory, but I'm thinking this is probably a badly-named Class that lives in the same package as your TitleWindow (so there's no import statement for it). Quick word of advice: if you want to get help on forums, following naming conventions wil make it easier for people wanting to help you to unravel what you've done. In this case, there may places that you have "illicit" functionality sidestepping best practice OOP communication, and they're not easy to identify because of the way you name thing.
But ultimately, I think if you go down to just one copy of the module and remove the event listener, you should be able to let go of your Module, which should let you release the VO.

Related

Flex spark list to render images

I am new in flex. Currently i am going to build a flex album, but I get a problem about
the render in image by spark list.
the problem is this last 2~3 thumbs can not be properly displayed, as you can see as follows:
http://www.j-rich.com/Problem
and it will look like:
http://www.j-rich.com/Problem/show.jpg
for the source code, you can right click and choose view source in the demo
Any suggestion will be appreciated, thank you very much.
Sincerely,
Yuan-Hsu Liao
Add cachePolicy="on" to your Image control in ItemRenderer. But I don't recomend to use this kind of huge images as thumbnails. Looks like Flash has some limitations in this field.
This is correct for me, my resolution is 1920*1080. I use FLEX SDK4.5.1, flashplayer 11.8.
By the way, you should better override the set data function in the ItemRenderer, and set img source in the function, like this:
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark">
<s:layout>
<s:VerticalLayout/>
</s:layout>
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.FlexEvent;
override public function set data(value:Object):void {
super.data = value;
var url:String = value as String;
img.source = url;
positionImg();
}
private function positionImg():void
{
this.ima.width = 136;
this.ima.height = 105;
this.ima.x = 0;
this.ima.y = 0;
}
]]>
</fx:Script>
<s:Group id="group" x="0" y="0" width="170" height="85">
<s:Image id="img" x="0" y="0" scaleMode="letterbox"/>
</s:Group>
</s:ItemRenderer>
ItemRenderer is recycled, so it's better to do some stuff in the set data()
Check whether ur Padding given is Correct...or else Adjust the same.

modifying the contents of a PasteOperation in Flex 4

I need to capture and modify the contents of a paste operation in my Flex 4 application. I am listening for the TextOperation.CHANGING event, pulling out the PasteOperation, and setting its textScrap property. Everything seems to be working, except that after I modify the textScrap, a newline character is added to the paste. I have created some sample code that illustrates the simplest version of the problem. I am not actually changing the copy, I am grabbing the existing textScrap's textFlow, creating a new TextScrap with it, and setting it on the PasteOperation. I did this to rule out TextFlow creation as the problem:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import flash.desktop.Clipboard;
import flash.desktop.ClipboardFormats;
import flashx.textLayout.edit.TextScrap;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.operations.PasteOperation;
import flashx.textLayout.tlf_internal;
import spark.events.TextOperationEvent;
use namespace tlf_internal;
protected function textArea_changingHandler(event:TextOperationEvent):void
{
if (event.operation is PasteOperation)
{
var pasteOp:PasteOperation = event.operation as PasteOperation;
pasteOp.textScrap = new TextScrap(pasteOp.textScrap.textFlow);
}
}
]]>
</fx:Script>
<s:TextArea id="textArea" changing="textArea_changingHandler(event)"/>
</s:Application>
Thanks in advance,
Gerry
This appears to be a bug that Adobe fixed in sdk 4.6.
As some additional information, replacing the pasteOp.textScrap line above with:
pasteOp.textScrap = pasteOp.textScrap.clone() used to throw a null pointer exception, and in 4.6 that works too now.
Also, this bug only appeared when the textarea / textinput was empty. if there was something already in there, everything worked as expected.

Can't see contextMenu on UIComponent, but on its subviews in Flex

I have a UIComponent (tried it also with Canvas) and some icons in it (sub views). On the UIComponent I defined some extra ContextMenuItems.
Now when I'm testing it, the context menu appears only on the subviews (icons) with a right-click.
I've checked the documentation but found nothing about required properties for using context menus.
Do you have any ideas why it's only on subviews?
This is probably happening because your UIComponent's (or Canvas') graphics are clean/empty. If the component doesn't have any "content" it will act as transparent, which means the click event will not be caught.
If you are using a Canvas there's a simple way to check this out, try to add some background color and it should work:
<mx:Canvas backgroundColor="#FFFFFF" backgroundAlpha="0.001"/>
I think this is what you're looking for:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="onCreationComplete()">
<fx:Script>
<![CDATA[
private function onCreationComplete():void
{
var menu:ContextMenu = new ContextMenu();
menu.customItems.push(new ContextMenuItem('weee'));
menu.customItems.push(new ContextMenuItem('weee2'));
menu.hideBuiltInItems();
canvas.contextMenu = menu;
}
]]>
</fx:Script>
<mx:Canvas id="canvas" backgroundColor="#000000" height="50%" width="50%" >
<s:Button label="blarg" />
</mx:Canvas>
</s:Application>
Notice how I'm creating a menu, then adding items, which then replaces the contextMenu property. This should work on any object that extends InteractiveObject.
What the problem was is the mouseEnabled="{false}" property in one of the parent-parent containers. I removed it and it works now.

How can I add multiple icons to the Spark TabBar control?

In the MX TabBar component, the iconField property allowed us to display different icons in each tab. In Spark, there does not seem to be an inherent way to add icons to the TabBar. Does anyone have an example of implementing icon support for Spark's TabBar? Is there a way to do this without extending the component?
Many thanks!
Hey after spending a week trying to follow multiple ways, (yours being top of the list) i found out a simpler and effective way to add icons to my tab bar, or any other component using skinning.
You dont need to create a custom component, just passing the icon and label through data.
http://cookbooks.adobe.com/post_Tutorials_for_skinning_Spark_ButtonBar_component_w-16722.html
As personally, i was using content navigator with my tabbar/viewstack, i passed the icon as icon instead of imageicon. you can make changes accordingly.
You'll have to create a skin for adding icons to Spark components; it is not as straightforward (IMHO) as Flex 3's MX components, though much more extensible.
Here are a few links which might help you get started:
Tour de Flex Tabbar examples
Custom Skin on Tabbar
Flex Tabbar with Skin
I believe I've come up with a solution, which I'm posting below for posterity. If anyone has a better way, I'd much appreciate the suggestion.
<!-- main app: TabBar implementation -->
<s:TabBar
dataProvider="{contentTabBarPrimaryDP}"
skinClass="skins.ContentTabBarSkin"/>
<!-- skins.ContentTabBarSkin: ItemRenderer implementation -->
<s:DataGroup id="dataGroup" width="100%" height="100%">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<s:itemRenderer>
<fx:Component>
<custom:IconButtonBarButton
label="{data.label}"
icon="{data.icon}"
skinClass="skins.ContentTabBarButtonSkin"/>
</fx:Component>
</s:itemRenderer>
</s:DataGroup>
<!-- skins.ContentTabBarButtonSkin: icon implementation -->
<s:HGroup
gap="3"
paddingBottom="3"
paddingLeft="3"
paddingRight="3"
paddingTop="3"
verticalAlign="middle">
<!--- layer 2: icon -->
<s:BitmapImage id="iconDisplay"
left="5"
verticalCenter="0" />
<!--- layer 3: label -->
<s:Label id="labelDisplay"
textAlign="center"
verticalAlign="middle"
maxDisplayedLines="1"
horizontalCenter="0" verticalCenter="1"
left="10"
right="10"
top="2"
bottom="2">
</s:Label>
</s:HGroup>
This solution uses a custom DTO object for the TabBar dataProvider which stores the label text as well as the embedded icon image as a class. I also had to extend the ButtonBarButton component to add an iconDisplay SkinPart, which looks like this:
[SkinPart(required="false")]
public var iconDisplay:BitmapImage;
This class also has getters/setters for the icon class property and sets the icon source, as such:
public function set icon(value:Class):void {
_icon = value;
if (iconDisplay != null)
iconDisplay.source = _icon;
}
override protected function partAdded(partName:String, instance:Object):void {
super.partAdded(partName, instance);
if (icon !== null && instance == iconDisplay)
iconDisplay.source = icon;
}
It's seems to be a bug/missed functionality of the your SDK version:
http://forums.adobe.com/thread/552543
http://bugs.adobe.com/jira/browse/SDK-24331
Anyway, thanks for the solution with skins - very helpful

Prefix for element not bound

I am a newbie in Flex development and using Flash Builder 4 with SDK 4. Now I get the error that "the prefix "fx" for element "fx:Style" is not bound" in line number 4.
I searched for it, and it has sth. to do with namespaces, but I can not solve it by myelf.
I have the file called "UserStory.mxml" in the directory "components" to place it via the main.mxml onto the screen:
<fx:Script>
<![CDATA[
import components.UserStory;
private function init():void {
var userStory1:UserStory = new UserStory();
userStory1.x = 100;
userStory1.y = 100;
userStory1.userStoryText = "test";
this.addChild(userStory1);
}
]]>
</fx:Script>
The file in which the error occurs in line no. 4:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="300" height="150" styleName="userstory">
<fx:Style source="styles/styles.css"/>
<fx:Text x="5" y="5" width="275" height="135" text="{userStoryText}" fontFamily="notes" fontSize="18"/>
<mx:Script>
...
</mx:Script>
</mx:Canvas>
Can someone tell me what's wrong?
As you suspected it is a problem with the namespace. MXML is just XML and in XML you can define namespaces and bind them to a URL. The namespaces are the part before the colon of an XML element and are usually defined on the enclosing element.
If you look at your MXML file you'll see one namespace declaration for the mx namespace:
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" ...
The declaration for fx is missing and that's what the compiler complains about. Add the appropriate definition and you should be fine (see this page for more details):
<mx:Canvas xmlns:fx="http://ns.adobe.com/mxml/2009" ...
Also, if you are using Flex 4 you should review the file as mx:Canvas is a Flex 3 component and as such not directly usable in Flex 4 apps. Have a look at the API docs of Canvas for the Flex 4 SDK.

Resources