I just noticed a strange behaviour while looking at my application in the Flash Profiler. When I click a button in my TitleWindow then the TitleWindow doesn't get garbage collected after it is removed. I have no idea why that is happening.
I've created a small example application, so you can try it out yourself:
Main.mxml
<?xml version="1.0" encoding="utf-8"?>
<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" pageTitle="Memory Leak (Spark)">
<fx:Script>
<![CDATA[
protected function openWindowBtn_clickHandler():void
{
removeAllElements();
addElement(new ExampleView());
}
]]>
</fx:Script>
<s:controlBarContent>
<s:Button label="Open Window" id="openWindowBtn" click="openWindowBtn_clickHandler()"/>
</s:controlBarContent>
</s:Application>
ExampleView.mxml
<?xml version="1.0" encoding="utf-8"?>
<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="100%" height="100%" title="Example View" close="closeHandler()">
<fx:Script>
<![CDATA[
import mx.core.IVisualElementContainer;
protected function closeHandler():void
{
var visualElementParent:IVisualElementContainer = parent as IVisualElementContainer;
if (visualElementParent)
visualElementParent.removeElement(this);
else
parent.removeChild(this);
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout verticalAlign="middle" horizontalAlign="center"/>
</s:layout>
<s:Button id="doSomethingBtn" label="Click me!"/>
</s:TitleWindow>
When you click "Open Window" and close the ExampleView without clicking the "Click me!" button in it then the GC kicks in and removes the ExampleView. However, when you click on "Click me!" and close the ExampleView afterwards, the ExampleView stays in memory forever.
I wasn't able to find the references in the Profiler which cause this behaviour. I hope someone knows a solution to this, otherwise Flex is creating a lot of memory leaks.
I may be wrong, but iirc EventListeners added in MXML are always created with a strong reference, which would prevent the Button from being GC'ed.
Have you tried adding the EventListener manually with setting it to being a weak reference? If you look at the list of EventListeners in the Debugger you should see something like a WeakMethodClosure if it was added with a weak reference.
One thing you're probably forgetting is that garbage collection isn't collecting unreferenced objects at the moment they loose the last reference. Usually the GC will collect the loose instances only after you create some object, but even than it's not obvious if it will do it in that moment. You can read more about it here:
About garbage collection
Or take a look at this presentation: Garbage Collection - Alex Harui
<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" pageTitle="Memory Leak (Spark)">
<fx:Script>
<![CDATA[
protected function openWindowBtn_clickHandler():void
{
removeAllElements();
addElement(new ExampleView());
}
protected function button1_clickHandler(event:MouseEvent):void
{
var o:Object = new Object();
System.gc();
}
]]>
</fx:Script>
<s:controlBarContent>
<s:Button label="Open Window" id="openWindowBtn" click="openWindowBtn_clickHandler()"/>
<s:Button label="Force GC" click="button1_clickHandler(event)"/>
</s:controlBarContent>
</s:Application>
Take a look at this. If you press the "Force GC" button a couple of times, it will collect the ExampleWindow. In a real world application that does something this happens without the need to call the System.gc() (in fact, it's not a good practice to call it), but after a while, so the things don't just disapear when you're done with them, they disapear when you're done, and Flash Player decides it needs to clean up.
Looks like the ExampleView is not getting Garbage collected because somehow some EventListener is added when "Click Me" is clicked.
The best way to avoid this is to
1. add Event Listener manually in createComplete event
2. Remove the EventListener in closeHandler
3. Remove the button from the container and set it to null
Now the ExampleView will be Garbage collcected
Related
I want to show the value of a ComboBox in it's Change event handler. The labelField is assigned dynamically, so that I don't know what it is when I run the event handler
cmbArea.labelField = qry.outFields[0];
I was hoping that I can have access to the value of selectedItem like:
protected function cmbArea_changeHandler(event:IndexChangeEvent):void{
// TODO Auto-generated method stub
Alert.show(event.target.selectedItem.value);
}
But it didn't work.
Any help would be much appreciated.
It's difficult to debug this without seeing your data provider code or the error message. However, it doesn't matter that the labelField is assigned dynamically. What you have will work if elements in your data provider have a property named "value". If the only property in your data provider is the one you assign as the labelField, you should be able to access it in your change handler with the following:
Alert.show(ComboBox(event.target).selectedItem);
<?xml version="1.0" encoding="utf-8"?>
<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">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import spark.events.IndexChangeEvent;
protected function comboBox_changeHandler(event:IndexChangeEvent):void
{
Alert.show(comboBox.selectedItem);
}
]]>
</fx:Script>
<s:ComboBox id="comboBox"
change="comboBox_changeHandler(event)">
<s:dataProvider>
<s:ArrayList>
<fx:String>Item 1</fx:String>
<fx:String>Item 2</fx:String>
<fx:String>Item 3</fx:String>
</s:ArrayList>
</s:dataProvider>
</s:ComboBox>
</s:Application>
I tested the following line and seems it works:
Alert.show(event.target.selectedItem[event.target.labelField]);
I have an AIR application. It should be moved around the screen with the mouse. In order to achieve this I use the event:
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, true,-2);
It should be activated with the lowest priority compared to inserted elements for example those that should be scrolled, clicked, etc.
I tried the solution shown below with the event priority set to -1 because there might happen 2 different events and my moving application event should be the last one to be serviced or shouldn't be serviced at all.
<?xml version="1.0" encoding="utf-8"?>
<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"
width="200"
height="200"
applicationComplete="init()">
<fx:Script>
<![CDATA[
import mx.core.Window;
import mx.events.ScrollEvent;
private function init():void {
this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown, true,-2);
}
private function onMouseDown(event:MouseEvent):void {
trace("clicked on stage "+event.currentTarget.toString());
if(event.currentTarget == stage){
trace("catched stage target");
this.nativeWindow.startMove();
event.stopImmediatePropagation();
}
}
function scrolledCanvasHandler(event:ScrollEvent){
trace("clicked on canvas "+event.currentTarget.toString());
event.stopPropagation();
}
]]>
</fx:Script>
<mx:Canvas x="29" y="34" width="80%" height="80%" backgroundColor="#343434" scroll="scrolledCanvasHandler(event)">
<mx:Label x="25" y="77" text="moving window, moving window"
fontSize="18" color="#FFFFFF" fontWeight="bold"/>
</mx:Canvas>
</s:WindowedApplication>
As you will notice the
event.stopPropagation();
doesn't work.
Perhaps my solution isn't the best suited to achieve this. Are there better solutions?
Chris
that's what i did in an app of mine:
<s:HGroup id="appTitleBar"
width="100%" height="35"
styleName="titleBar"
mouseDown="nativeWindow.startMove();"
doubleClickEnabled="true"
doubleClick="nativeWindow.minimize();"
contentBackgroundColor="#313131"/>
click (+drag) on this HGroup will drag the window. duobleclick will minimize it.
edit
don't make your whole app draggable this will only confuse the user.
and btw priority should be positive not negative - but also don't mess with this. not expected behavior for anyone.
I've got a problem while migrating my TabNavigator from Flex 3 to Flex 4.5. Stripped to the bare minimum the following code will produce the bug, namely that the second child of the TabNavigator fails to be created properly:
<?xml version="1.0" encoding="utf-8"?>
<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"
>
<fx:Script>
<![CDATA[
protected function over():void
{
trace('over');
}
protected function content_one_init():void
{
content_one.enabled = true;
navigator.selectedIndex = 1;
}
]]>
</fx:Script>
<mx:TabNavigator
id="navigator"
creationPolicy="auto"
width="100%" height="100%"
>
<mx:VBox
id="content_one"
enabled="false"
creationComplete="content_one_init()"
label="One"
mouseOver="over()"
/>
<mx:VBox label="Two">
<mx:Label text="Content Two" />
</mx:VBox>
</mx:TabNavigator>
</s:Application>
What I see, upon launching, is a TabNavigator with its second tab selected but no content instead of the expected "Content Two" label. The navigator.selectedIndex = 1; instruction is there just for comfort, the bug appears also if you select the second tab with the mouse after launching.
Now comes the fun part: if I do any of the following, the second child gets created:
set creationPolicy="all" on content_one (this is expected),
comment content_one.enabled = true,
remove enabled="false" on content_one,
remove mouseOver="over()" from content_one (this one is really really weird, as the handler is never called anyway)
Is this really a Flex bug, or is there something I'm missing? I'm using Flex 4.5.0.20967, and all of this worked well in Flex 3.5.
Thanks.
This has been confirmed as a bug at Adobe's and filed in their bug database
I am having an issue with my ItemRenderer, which I am using for a spark List. My code is the following:
I have this list:
<s:List
id="productSetList"
dataProvider="{ model.productSets }"
change="model.selectSet( productSetList )"
height="100%" width="100%"
borderVisible="false"
itemRenderer="SideBarItemRenderer" top="20" left="15">
</s:List>
and my itemRenderer is:
<s:ItemRenderer
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/halo"
width="160" height="175" autoDrawBackground="false" buttonMode="true" useHandCursor="true"
click="click(event)" cacheAsBitmap="true"
>
<fx:Script>
<![CDATA[
import com.png.vm.model.vos.ProductSet;
protected function click(event:MouseEvent):void
{
trace('arthur');
}
]]>
</fx:Script>
<s:BitmapImage source="{ProductSet(data).image}" width="160" height="175"/>
</s:ItemRenderer>
The thing is, if I scroll the list, and click on an item, it does not trace 'arthur' ! Why is this so ? I must trace that all the time that someone clicks in the list!
EDIT:
If I remove change="model.selectSet( productSetList )" in the list, it works!! but I cannot remove that, some suggestions ? How can I switch that to another function?
Trace only works when you are debugging/using the debugging version of Flash Player. Make sure you are using that. If you want a pop-up message use Alert.show("message")
For more information about trace() check out:
http://livedocs.adobe.com/flex/3/html/help.html?content=logging_08.html
And Alert.show(): http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00001965.html
If you are running debug player. Try originating the click event from an embedded <s:Group> this way whatever you add in here beyond the bitmap will still trigger the click event.
Something like:
<s:Group width="100%" height="100%" click="click(event)" >
<s:BitmapImage source="{ProductSet(data).image}" width="160" height="175"/>
</s:Group>
I've definitely had click events work for me inside of ItemRenderers before
My apologies, I have finally solved it. The problem was that inside the function,model.selectSet, I was calling List.change; I was messing the list up! My function was as follows:
public function selectSet(list:List):void {
list.layout.verticalScrollPosition=100;
// another stuffs
}
So, I just removed the line : list.layout.verticalScrollPosition=100; and now it`s working fine.
Thanks for all the help!
I want to handle the keydown event globally in my application and as such have added the event handler to the top most DisplayObject in the display list. However, the event doesn't seem to fire unless the cursor focus is on a Textbox or Datagrid or the like.
How can I have key presses handled globaly by the application?
In response the OP's comment above, here's how to hook it up correctly, namely in the application's creationComplete event:
<?xml version="1.0" encoding="utf-8"?>
<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"
creationComplete="initApp();">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.managers.SystemManager;
public function initApp():void {
systemManager.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown)
}
public function onKeyDown(event:KeyboardEvent):void {
pressedKey.text = event.charCode.toString();
}
]]>
</fx:Script>
<mx:Label x="486" y="63" text="You pressed: "/>
<mx:Label id="pressedKey" x="577" y="58" width="122" text=""/>
</s:Application>
stage.addEventListener(...) ?
First, you won't detect any keystroke until your flex application get the focus.
You can do it automatically by calling a js script at startup that will set the focus on the application
Once done, I would recommand you to add your key listener on systemManager. Doing that will allow you to handle keydown event globally.
ExternalInterface.call('function browserFocus(){document.getElementById(\'APPLICATION_NAME_HERE\').focus();}');
This works only with IE and Opera.