1

I have a little problem.

The environment

Environment: Flex 3 (actually updated to Flex 4 SDK, but still using mx instead of spark)

IDE: Flash Builder 4.6.

Browser: Firefox 14.0.1

Flash player version: FP 11 Debugger - 11.2.202.235

States syntax: Flex 3 style syntax.

The application

I have an application.

This application has several states.

One of the states has, in turn, a "wrapperComponent" with three different states: the base state (i.e. ""), "firstState" and "secondState".

Simplifying it a bit, pretty much what each of those states contains is a different custom component, like this:

  • "wrapperComponent" -> "base comp."
  • "firstState" -> "first comp."
  • "secondState" and "second comp."

The following image shows the situation more clearly:

Hierarchy of the application components and states

Then, when the user is using the "wrapperComponent" it first reaches the "firstState", then he uses the "base state" and after that, he goes to the "secondState", like the following picture shows:

Flow of states in the wrapper component

So, the problem is...

So, the problem is that when the user reaches the third step in the flow, i.e. when he reaches the "secondState" of the "wrapperComponent" sometimes the "second comp." appears, and sometimes it does not appear.

The findings so far...

Discussion in Adobe forums: http://forums.adobe.com/message/4621058 3

After struggling with this for about two weeks, I came to the semi-conclusion that somehow, the internal flow of events in ActionScript is halted.

This means that, when my custom component called "second comp." doesn't show up on the screen:

  • the transition from the "base state" to the "secondState" is stopped/paused/halted,
  • only some of the events of the "second comp." are dispatched ("preinitialize", "initialize", "add", "added" are dispatched) but some others are not: "addedToStage" and "creationComplete" are not dispatched.
  • the variable "activeEffects" of both the "base comp." and the "second comp." contains effects that are not played (like "RemoveChildActionInstance" or "AddChildActionInstance") and
  • the variable "updateCompletePendingFlag" variable of these same components' value is "true", meaning that they haven't finished updating.

The ugly hack

I found a really horrible way of circumventing the problem, with this workaround:

  • I set up a 500ms Timer that starts counting when the state has to change from "baseState" to "secondState" (that is, from step 2 to step 3 in the image above)
  • When this Timer sets off, I check whether or not the components (i.e. "base comp." or "second comp.") have any pending effect or if their "updateCompletePendingFlag == true".
  • If the check yields true, then I force the call of a validateDisplayList() method on that particular component.

The code looks something similar to this:

...
if ( toState == STATE_SECOND ) {
var theTimer    : Timer = new Timer(500,1);
theTimer.stop();
theTimer.addEventListener(
TimerEvent.TIMER_COMPLETE, 
theTimer_complete_Hdl, 
false, 0, true
);
theTimer.start();

currentState = STATE_SECOND ;
}
...

And the timer calls this function:

private function theTimer_complete_Hdl(event:TimerEvent):void
{
if (secondComp.updateCompletePendingFlag)
{

secondComp.invalidateDisplayList();
secondComp.validateDisplayList();
secondComp.validateNow();
this.invalidateDisplayList();
this.validateDisplayList();


}
...
} 

Problems...

This is really dirty, unelegant code. Plus, it's giving me bad user experience, because it takes more than one second to refresh the screen, and the movement is not swift.

My guess is that it's somehow negatively affecting the performance.

**Any ideas on what could be happening or a better way to force FlashPlayer to continue playing the effects instead of killing them and forcing an updateDisplayList?**

Thank you guys!!! :-)

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
JoseVelas
  • 27
  • 5
  • 1
    Can you share the code for your component? I think it potentially has to do with virtual layouts or your states not cleaning up correctly. On an aside, are you using flex 3 state or flex 4 state syntax? – badunk Aug 23 '12 at 20:58
  • Thanks @badunk. Well, actually it's a very large application and I cannot reveal the code. I posted the piece of code that controls this hack. I'm not an expert, so could you please direct me to the use of virtual layouts or any way in which I can control the states' flow? Is it there a global function or property that can let me control the flow of states? I'm using Flex 3 state syntax. Everything is Flex 3 flavor. The only thing is that the SDK is the latest version (4.6) – JoseVelas Aug 23 '12 at 21:02
  • 1
    ah I understand, perhaps it is not virtuallayouts then. I read your forum post and in situations like these, I like to use a library called FlexSpy (http://code.google.com/p/fxspy/). It allows you to inspect (and even modify) the measuredWidth/Height of components to allow you to debug what's going on. It makes sense that addedtoStage and creationComplete is not firing because the width and height are 0, but why it is 0 seems unintuitive to me in the context you gave. I would also try to make the wrapper with static oversized dimensions and children with static dimensions too. – badunk Aug 23 '12 at 21:05
  • @badunk: It seems that the error is gone! (I've become very skeptical lately, because due to the erratic behavior of this error, sometimes I think that I have the solution, and then it vanishes). But, just in case, I deeply love you and want to marry you. My custom component ("second comp.") was defined, in the state, as having width=100% and height=100%. Plus, all of its mxml children's sizes were defined that way. It seems that when the state changes, Flex loses the reference for measuring the width (probably because everything's defined with width=100%), hence the component didn't appear!!! – JoseVelas Aug 23 '12 at 22:10
  • @badunk: (continued) It was as silly as that... Two weeks of my life :S . I'm going to wait till tomorrow. If the error completely disappears, you'd better tell your mom you're mine. LOL. thanks a lot. – JoseVelas Aug 23 '12 at 22:10
  • 1
    lol great thanks :) you should answer your own question and accept as answer. You can also upvote my help ;) – badunk Aug 23 '12 at 23:15
  • hehe actually I'm not sure of what the _"correct"_ answer is. I mixed both your suggestion (_using static size_) and _forcing a `updateDisplayList()` method call_. So, I consider it's still kind of a hack. On top of that, I still get minor annoyances (jerking motion, little halts, things that need to be forced visible to appear...) So i'm unsure of whether answering it with this info. I'm upvoting your help, nonetheless. I didn't know you could do that, LOL. – JoseVelas Aug 24 '12 at 17:19

1 Answers1

0

I seriously thought this was a FlashPlayer's bug.

Obviously, the most likely answer was that it was MY mistake, as indeed it was.

I spent so much time on this, not because it was a difficult one, but because the project is complex and the problem was in a class not directly related to the one having the problem.

This was a fairly easy fix, after pinpointing the origin.

What was causing the problem?

I was using, alongside myCustomComponent, another custom class in the previous state (i.e. within the "base comp." in the "base state").

This customClass extended the flex mx Label class. In the commitProperties() overridden method it didn't set a flag false and it called invalidateProperties() again. Of course, this was a mistake, and was provoking an infinite loop in this class (with five instances in the "base comp." component.

This didn't prevent the application from keeping running. It didn't halt it. But it prevented the effects defined in the transition from running and the myCustomComponentinstance from finishing creation.

The pseudo-code in "base comp." that had that little mistake was something like this:

override protected function commitProperties():void
{
    super.commitProperties();
    if (somethingChanged)
    {
        //1: I wasn't setting somethingChanged=false
        
        //2: I needed to call invalidateProperties() again, like this:
        invalidateProperties();     
    }
}

The correct version would be:

override protected function commitProperties():void
{
    super.commitProperties();
    if (somethingChanged)
    {
        //1: I added this line:
        somethingChanged=false;

        //2: I still need to call invalidateProperties() again, like this:
        invalidateProperties();     
    }
}

Or even better:

override protected function commitProperties():void
{
    if (somethingChanged)
    {
        //1: I added this line:
        somethingChanged=false;

        //2: Instead of invalidating the properties, doing some  
        //  processing and calling super.commitProperties()
        ...; //  (some processing)
    }
    super.commitProperties();       
}
Community
  • 1
  • 1
JoseVelas
  • 27
  • 5