The Fading of the Flex Framework

December 12th, 2011

I do not usually write “controversial” business posts, but I wanted to type out my thoughts after some more information has flowed out from Adobe since last month’s infamous post.

Here are the important new links:

I was surprised the question that got the most votes was how Adobe could restore confidence in the developer community. To me, that is mostly setting oneself up to receive a lot of marketing spin. It’s like asking the waiter if the special is any good. (Hint: they aren’t going to say it is awful or even meh — at worst you’ll get “good, but not my personal favorite.”)

So let’s get into the video. At the 0:01:25 mark…

“We have decided to restructure the company and to focus the priorities in such a way that we don’t feel like we can run Flex the way that we have before where it is completely resourced and funded by Adobe. And as David has alluded to, where we have an ambition to run it as a standalone business that is actually profitable in its own right. And that is one of the things that is fundamental in guiding the change that we have made in deciding to move the project into Apache and into more of a community development model.”

So we find out that Flex is a money loser for Adobe. And this is not a Google + YouTube or Amazon + Kindle Fire money losing kind of thing. Adobe’s business decision is to make cuts and reduce resources. But is this like a skeleton crew? You know, kind of like that Star Trek episode where only Dr. Crusher and Captain Picard were left to run the Enterprise?

Which brings us to the money question — the one I would have put all 10 of my votes behind. It was asked like this:

“How many Adobe engineers will be working for the benefit of FLEX (SDK, compilers, tooling, runtimes, documentation, evangelism etc) after the transfer to the ASF.”

Better still to ask it more direct, like this:

“How many Adobe employed developers will be working fulltime on the Flex SDK code?”

In the link to the video above at 0:15:45, the general question is answered like this:

“We have, moving forward, on things related to Flex, dozens of people still in the company. I mean we don’t disclose exact numbers, because obviously it fluctuates from time to time as we move people around to assignments inside the company, but there definitely are dozens of people that are still engaged in various aspects of our Flex agenda. So whether that be direct contributions to Apache, whether it be honoring our maintenance and support contracts that are associated with any fixes for the Adobe releases of Flex, our evangelism and support organization, documentation.”

“We are continuing to have dedicated staffing across all of those functions and, like I said, that comes out to dozens of people. Still a significant effort. It is less than what it was before these changes. So the point of what I said before, we did make the decision, as part of this transition, that we did not think we had a viable path forward for Flex as a standalone profitable business within the context of our new strategy.”

“And that is why we have made the changes we have made to actually shift to the Apache and the community development model so that we can ensure that there is a successful path forward for Flex with dedicated resourcing and staffing across all of those functions, albeit at a reduced level from what we had committed internally. And it is our sincere hope that, with the community engagement that we expect to enable through Apache, and hopefully contributions from any of you, that we will be able to ensure a thriving and innovative and lively path forward for Flex within the context of Apache.”

Not going to happen.

I’m sorry, I don’t intentionally want to be negative here, but seriously. Who thinks the Flex SDK will thrive rather than just survive over the next 2-4 years. Aren’t most freelance Flex developers like me — meaning the vast majority of your time is spent coding client’s projects (i.e. billing hours). You are invested in Flex so you dutifully file bug reports (good ones too, where you spend an hour boiling it down and submit a small .fxp that illustrates the problem.) Knowing how others have helped your understanding, you feel the desire to give back, but your quality, code-sharing blog posts are less in number than you thought. And when you do find some extra time, you spend it sharpening your secondary skill-set in the event your primary one gets… oh, never mind.

If the 3rd party Flex component market never took off, why does Adobe think there is a large community willing to spend time (which equals money) doing what Adobe is no longer willing to do — fix and further the hot mess that is the Flex SDK?

Perhaps they think that because the Flex architectural framework space (Swiz, Robotlegs, Parsley, etc.) prospered, the same can hold true for the core framework. I readily admit that I don’t fully understand open source, but I get that developers donate time they could be billing because… ummm, betterment of… oh forget it.

But what’s easily observable is the road littered with abandoned code. I gladly pay for code like Greensock’s Tweening Platform. And though my $100 only buys probably 1 to 1.5 hours of coding time, hopefully there are enough of us that it creates a nice profitable business for Jack. With architectural frameworks, I guess there is the thrill and perhaps the business need (like with Universal Mind, or EffectiveUI)? But is there really a thrill in basically working for Adobe but without the monetary compensation, kind of like when they quit giving Milton a paycheck in Office Space. The Flex 4 SDK was largely about Flash Catalyst and it just got discontinued. Yeah, untangling all of that sounds like fun.

So perhaps the other reason is true then – the business need. I have made the most money in my career specializing in Flex development. Should we all donate 8 weeks a year to keep our favorite SDK around? That’s more than $20k of billable hours. I was content paying Adobe $550 every 12 months for my dot 5 Master Collection upgrade believing they would dedicate the resources. That only buys 5-7 hours of development time, however, and apparently that’s not paying Adobe’s bills.

Adobe is exiting the enterprise framework business and, according to the video, does not plan to reenter it with an HTML solution. Which answers the question of why did Adobe make this announcement about Flex before presenting their alternative.

Developers will still make great money building enterprise apps with the Flex framework. Better devote time to sharpening that secondary skill-set during the countdown, however.

A Faster Splash Screen for AIR on iOS

September 15th, 2011

I noticed that some AIR apps on my iPad2 show their splash screen instantly, whereas others (Politifact, Neptune) show a blank black screen for 2-3 seconds before their splash screen. The latter are using the Flex framework, although this is not completely a Flex issue, but rather any AIR app that uses a Flash preloader. Interestingly, the excellent Machinarium app shows its “Amanita Designs” splash screen instantly, but then shows a blank black screen for 10 seconds (!) before showing the next splash screen.

A client’s app I am developing is using the Flex framework, so that is what I will be posting about.

In a Flex app, you specify a splash screen image like this:

  1. <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
  2.     xmlns:s="library://ns.adobe.com/flex/spark"
  3.     splashScreenImage="@Embed('assets/splash.png')"
  4.     splashScreenScaleMode="letterbox">

Or, for a more advanced method, you can extend spark.preloaders.SplashScreen as described by Jason San Jose’s article in order to provide a different splash screen based on dpi, orientation, or device.

However, if you want an instant splash screen on iOS, you have to make use of their launch image method. For my client’s iPad app, it meant creating Default-Portrait.png and Default-Landscape.png files as described in this Adobe documentation on iOS launch Images. You place those images directly in the src folder of your Flex mobile application.

So having those launch images results in an instant splash screen, but it will be followed by a blank black screen if you do not also specify a splash screen image in Flex (or have some Flash preloader display a splash screen image, which is what the non-Flex Machinarium app is lacking).

My single splash image was 1280 x 1280 in order to support both iPad and Android in both orientations. I made the Default-Portrait.png 768 x 1004 and the Default-Landscape.png 1004 x 768 as specified in the Adobe documentation above.

After doing this, however, I noticed that my launch image and my splash screen image were not positioned exactly the same. This produced a noticeable jump of 20 pixels when the launch image became the splash image.

When doing a search, I found this documentation that states:

“For an application that is not full-screen, the top 20 pixels of the default image art are ignored. The iPhone displays its status bar over the 20 pixel-wide rectangle at the top of the default image. In a landscape-orientation application, this region corresponds to the left 20 pixel-wide rectangle of the Default.png file (which displays on the top in landscape mode). In a portait-orientation application, this region is the top 20 pixel-wide rectangle of the Default.png file.”

So what I had to do was open my 1280 x 1280 splash screen image, and clip it to 768 x 1024 (not 1004) at point 256, 118. Then save that as my Default-Portrait.png. For the landscape launch image, I clipped it to 1024 x 768 at point 128, 246.

That made it so that the launch image and the splash screen image were positioned exactly the same. The user could not see a difference as the app changed between the two. This removed the 2-3 seconds of having a blank black screen before the splash screen appeared.

Using Charles Proxy to Inspect Mobile Device Traffic

September 8th, 2011

I use Charles to inspect the internet traffic of desktop applications all the time. It can also be used to monitor traffic to your mobile devices.

The details are on the Charles website:

An even better description can be found in this article from mediarain.

Achieving a High Number of Interactive Markers with the Google Maps for Flash API

June 2nd, 2011

For the past year, I have been working on a client project that utilized the Google Maps for Flash API. One issue that I hoped to address was the sluggishness that results from instantiating 100+ instances of com.google.map.overlays.Marker. The forums have a few threads discussing this topic. Digging into them, you immediately get the idea to not use Google’s Marker class, but rather a single instance of a custom class you create that extends OverlayBase. This class would then create markers as instances of Sprite. You would override the OverlayBase’s positionOverlay() method to reposition your markers. This technique allowed us to achieve a few hundred interactive markers before the application became noticeably sluggish.

Getting to the next level of supporting 1,000+ interactive markers, however, was a much more difficult task. There was this thread, where Jonathan Wagner described the use of bitmap tiles. He went further by providing an example and sharing his code. Unfortunately, while the example achieved an ultra-high number of markers, the markers were not interactive. However, it certainly pointed me in the right direction.

Basically, what you need to do is create 256 x 256 bitmaps that will exactly overlay the 256 x 256 bitmap tiles that Google serves up for their map.

The 256 x 256 map tile from Google combined with the one you create in ActionScript

When each marker was its own instance of Sprite, you could easily register event listeners for the MouseEvents. However, with a bitmap that can contain several markers, you have to get more creative. More on that below.

The classes are:

  • Marker.as
  • MarkerGroup.as
  • MarkerTileLayer.as
  • Tile.as

Your application creates an instance of MarkerTileLayer. It also creates an instance of com.google.maps.overlays.TileLayerOverlay, passing in the MarkerTileLayer as a parameter.

PerformanceMarkers.mxml

  1.     markerTileLayer = new MarkerTileLayer(map);
  2.     tileLayerOverlay = new TileLayerOverlay(markerTileLayer);
  3.     tileLayerOverlay.addEventListener(MapEvent.TILES_LOADED, tileLayerOverlay_tilesLoadedHandler);
  4.     map.addOverlay(tileLayerOverlay);
  5.     addMarkers();

The TILES_LOADED event listener is important because it is how we keep the number of Tile instances under control by calling markerTileLayer.disposeOffscreenCachedTiles().

In the addMarkers() method, I create instances of Marker. The constructor takes two parameters: the LatLng and BitmapData. I add the event listeners for Marker.MARKER_CLICK and Marker.MARKER_HOVER directly to the Marker instances.

So my application only deals directly with two of the four classes in the above list: MarkerTileLayer and Marker.

MarkerTileLayer deals with the other two classes: MarkerGroup and Tile.

MarkerGroup is pretty simple. It is a group of Marker objects that are contained by a LatLngBounds. Both of these are passed into the MarkerGroup constructor. It then exposes a public method whose signature is containsMarker(marker:Marker):Boolean.

Tile is more complex. Tile extends Sprite and is the DisplayObject that gets returned from MarkerTileLayer.loadTile().

MarkerTileLayer has a setter for the markers Array. This is where I break up the Markers into MarkerGroups. I use MarkerGroups so that the MarkerTileLayer.loadTile() method does not have to cycle through every Marker instance to check if it should be rendered onto that Tile. Instead, it can check a smaller number of MarkerGroups. If the MarkerGroup’s latLngBounds does not intersect the Tile, then the entire MarkerGroup can be skipped.

When a marker is clicked, figuring out which marker is tricky. You just need to register an event listener on the Marker instance for Marker.MARKER_CLICK, but underneath the hood MarkerTileLayer has event listeners on the Tile. When a Tile is clicked, it must determine which Marker. It does this in the method Tile.getTopmostMarkerAtPoint().

One issue I have found is that if a Marker has a pixel with an alpha value of less than 1.0, and that pixel is the one clicked, and the Marker overlaps another Marker, then the Tile.getTopmostMarkerAtPoint() does not return the Marker (and thus the Marker does not dispatch any event).

Screenshot of PerformanceMarkers

I have commented the code pretty well. Here are the application and source code. Be sure to try a larger number of markers than the default 500. On my machine, performance is excellent up to 5,000 and still very good at 10,000 and 20,000 (with only a slight stutter when zooming).

A major challenge is still animated markers. I have a solution to accommodate limited animation (4 frames) which I hope to go over in a future blog post. Basically each Tile has 4 BitmapData objects and swaps them out on the ENTER_FRAME event.

Gotcha on Modifying Objects in a Sorted ArrayCollection

October 14th, 2010

I experienced a problem today where iterating over the objects in my ArrayCollection was producing some strange results. I’ve boiled the issue down to the following example:

Book.as

  1. package com.curiousfind.testflex4
  2. {
  3.     [Bindable]
  4.     public class Book
  5.     {
  6.         public var title:String;
  7.         public var price:int;
  8.  
  9.         public function Book(title:String, price:int)
  10.         {
  11.             this.title = title;
  12.             this.price = price;
  13.         }
  14.     }
  15. }

In the application, I setup an ArrayCollection with a sort:

  1. private function setup():void
  2. {
  3.     var array:Array =
  4.     [
  5.         new Book("Professional Adobe Flex 3", 49),
  6.         new Book("Flex 4 in Action", 49),
  7.         new Book("Enterprise Development with Flex", 54),
  8.         new Book("ActionScript 3 Cookbook", 39),
  9.         new Book("Essential ActionScript 3", 54),
  10.         new Book("ActionScript 3 Bible", 49),
  11.         new Book("Advanced ActionScript 3 with Design Patterns", 49)
  12.     ];
  13.     books = new ArrayCollection(array);
  14.     var sort:Sort = new Sort();
  15.     var sortField1:SortField = new SortField("price");
  16.     var sortField2:SortField = new SortField("title");
  17.     sort.fields = [sortField1, sortField2];
  18.     books.sort = sort;
  19.     books.refresh();      
  20. }

Datagrid with the book prices

Now I have a button that will iterate through the books and increase each book’s price by $10

  1. private function button1_clickHandler():void
  2. {
  3.     for each (var book:Book in books)
  4.         book.price = book.price + 10;    
  5. }

However, when button is clicked I get this:

Datagrid showing books with incorrect prices

All the book prices except one are incorrect. So what happened?

The Book class is marked as bindable so that the books ArrayCollection will receive PropertyChangeEvent.PROPERTY_CHANGE whenever one of the properties of the Book object is changed. (That is setup in the startTrackUpdates() method of ArrayList.as if you are looking at the Flex SDK source). The ArrayCollection re-sorts itself when one of the objects it contains dispatches a PropertyChangeEvent. So the for each loop is not going through each item of the ArrayCollection. It hits some Book objects multiple times and some it misses.

There are a couple of solutions. The simplest is this:

  1. private function button2_clickHandler():void
  2. {
  3.     var array:Array = books.toArray();
  4.     for each (var book:Book in array)
  5.         book.price = book.price + 10;    
  6. }

By calling the ArrayCollection.toArray() method I get an array that won’t change on me as I iterate through it.

Another way would be to remove the [Bindable] metadata from the Book class and do this:

  1. private function button1_clickHandler():void
  2. {
  3.     for each (var book:Book in books)
  4.         book.price = book.price + 10;
  5.     books.refresh();
  6. }

Removing the [Bindable] metadata tag means that the compiler does not make the Book class extend EventDispatcher nor rewrite the title and price properties to be getters and setters. Therefore the ArrayCollection will not be notified via PropertyChangeEvent.PROPERTY_CHANGE that the objects it contains have been updated. Since the ArrayCollection won’t be notified, it in turn will not re-sort as we change a Book object’s price. It also will not dispatch CollectionEvent.COLLECTION_CHANGE and the DataGrid will not update. To get the ArrayCollection to re-sort and the DataGrid to update with the new prices, I would have to manually call the ArrayCollection.refresh() method.

Here is the actual Flex application that demonstrates the gotcha and the toArray() method that works:

The Flash plugin is required to view this object.

This is the Kind of Thing I Would Have Done in Flash

February 14th, 2010

Flash owns rich internet applications, and the Flex framework is where I spend a lot of my time now. But I still do projects for clients in Flash like banner ads (using Greensock’s Tweening Platform), online presentations, and interactive widgets.

Since I am good at Flash, I tend to favor it over JavaScript for cranking out those rotating banners and such. However, it is neat to see what things are now being done via JavaScript where there used to be a SWF. And it is good for those skilled with Flash to ask the question of whether a SWF is still the best choice for these things.

Apple’s iTunes countdown is the example that prompted this post. When I saw it I was curious about how it worked. Here’s the 53×6180 png file.

Sure enough, the banner works on the iPhone. Although the animation is considerably less smooth on my iPhone 3GS than it is on my 3-year-old MacBook Pro (with a hard reset of the iPhone just to be sure.)

When Flash Player 10.1 is out on other mobile devices it will be interesting to see how it performs with animation like this.

iTunes counter on iPhone

AS3 and Big Numbers

January 27th, 2010

I ran into a problem when transferring large numbers between the client and server. They would send me a number and when I sent it back to them it would be different. Turns out ActionScript 3 has problems with big numbers. Here is a quick example. The expected output would be 129090238754375001.

  1. var string:String = "129090238754375000";
  2. var number:Number = Number(string);
  3. var nextNumber = number + 1;
  4. trace(nextNumber);

Here are the variables after execution. Notice that both number and nextNumber are 129090238754375008.

Screenshot showing variables at breakpoint

And the output window:

Output window showing 129090238754375000

So int has a range of -2,147,483,648 to 2,147,483,647 and uint has a range of 0 to 4,294,967,295. While Number can represent integers outside the range of int and uint, it has precision limitations.

The solution in our case was that, since we were not doing any math and just using it as an identifier, we changed the server code to use a string instead.

Ultimate Reset of a Loaded AS3 MovieClip

January 27th, 2010

I had a project that loaded individual SWF files for playing in sequence. There were the typical play, pause, previous, next buttons. The SWFs were loaded via Loader. An MP3 file was associated with each SWF.

If the loaded SWF had no nested movieclips, everything went smoothly with movieClip.stop(), movieClip.play(), etc. However on this project, there were nested movieClips in the SWF, so these all needed to be stopped when the user pressed pause, and restarted when the user pressed play.

  1. private function stopMovieClipAndChildren(content:DisplayObjectContainer):void
  2. {
  3.     if (content is MovieClip)
  4.         (content as MovieClip).stop();
  5.     if (content.numChildren)
  6.     {
  7.         var child:DisplayObjectContainer;
  8.         var n:int = content.numChildren;
  9.         for (var i:int = 0; i < n; i++)
  10.         {
  11.             if (content.getChildAt(i) is DisplayObjectContainer)
  12.             {
  13.                 child = content.getChildAt(i) as DisplayObjectContainer;
  14.                 if (child.numChildren)
  15.                     stopMovieClipAndChildren(child);
  16.                 else if (child is MovieClip)
  17.                     (child as MovieClip).stop();
  18.             }
  19.         }
  20.     }
  21. }
  1. private function playMovieClipAndChildren(content:DisplayObjectContainer):void
  2. {
  3.     if (content is MovieClip)
  4.     {
  5.         var movieClip:MovieClip = content as MovieClip;
  6.         if (movieClip.currentFrame < movieClip.totalFrames) // if the main timeline has reached the end, don't play it
  7.             movieClip.play();
  8.     }
  9.     if (content.numChildren)
  10.     {
  11.         var child:DisplayObjectContainer;
  12.         var n:int = content.numChildren;
  13.         for (var i:int = 0; i < n; i++)
  14.         {
  15.             if (content.getChildAt(i) is DisplayObjectContainer)
  16.             {
  17.                 child = content.getChildAt(i) as DisplayObjectContainer;
  18.                 if (child.numChildren)
  19.                     playMovieClipAndChildren(child);
  20.                 else if (child is MovieClip)
  21.                 {
  22.                     var childMovieClip:MovieClip = child as MovieClip;
  23.                     if (childMovieClip.currentFrame < childMovieClip.totalFrames)
  24.                         childMovieClip.play();
  25.                 }
  26.             }
  27.         }
  28.     }
  29. }

(my google search landed me here before writing my own)

Clicking the previous button presented a problem however. Calling movieClip.gotoAndStop(1) on the movieClip (and even all its children) was not putting it back to its “new out of the box” state. I needed a way to do an ultimate reset of the movieClip but since it was a loaded SWF, I didn’t have the class to do:

  1.  movieClip:MovieClip = new SomeLoadedMovieClip();

However, after reading this blog I found out how.

  1. private function resetMovieClip(movieClip:MovieClip):MovieClip
  2. {
  3.     var sourceClass:Class = movieClip.constructor;
  4.     var resetMovieClip:MovieClip = new sourceClass();
  5.     return resetMovieClip;
  6. }

Use it like this:

  1. stopMovieClipAndChildren(movieClip);
  2. // remember to remove all event listeners from movieClip and anything that references it
  3. // remove movieClip from the display list
  4. removeChild(movieClip);
  5. // reset the loaded movieClip by creating a new instance of it
  6. movieClip = resetMovieClip(movieClip);
  7. addChild(movieClip);

Learned Something About Events in ActionScript 3

July 11th, 2009

I was recently presented with a multiple choice question that went something like this:

Events in ActionScript 3 represent what type of code?

  1. Synchronous Execution
  2. Asynchronous Execution
  3. Object Oriented Programming
  4. Procedural Programming

Thinking how events allow for loosely-coupled components, I clicked answer #3 and moved on. Like every Flex developer, I use custom events that extend the Event class and deliver a payload to the event handler function. However, here recently, I’ve been working on a project for a client where they used no custom events whatsoever. It is a large project and, architecturally-wise, it is really a mess. Every object has a reference to every other object and they all call methods on each other, or worse, on each other’s children.

Anyway, as I thought back to the question it got me wondering if, despite my constant use of events, I had a good understanding of how Flash’s eventDispatcher class (whose code, unlike the open-source Flex framework, is part of playerglobal.swc and cannot be viewed) works.

I did a quick test in the Flash IDE

  1. addEventListener("myEvent", myEventHandler);
  2. dispatchEvent(new Event("myEvent"));
  3. trace("hello");
  4.  
  5. function myEventHandler(event:Event):void
  6. {
  7.     trace("hello from myEventHandler");
  8. }

Which produced the result:

hello from myEventHandler
hello

So the correct answer to the question above is 1. Synchronous Execution. (Added: or not… see discussion in the comments).

Calling the dispatchEvent method on EventDispatcher (or any class that extends it) causes EventDispatcher to immediately look up the functions that were registered as event handlers and call them in the order they were registered (or in the order of the priority argument passed in when registering the handler via addEventListener).

I don’t know why I didn’t realize this before. I guess I just hadn’t walked it through in my mind or thought events got queued up or something with handlers called when that queue was processed, perhaps on the next frame.

Adventures in Freelancing: Death of the MacBook Pro

March 20th, 2009

On Sunday, I went into the home office to work on my third gig since moving to full-time freelancing.  My out-of-warranty-but-still-very-new-to-me MacBook Pro with its 4GB of memory, 1920 x 1200 screen, and nearly $3,000 price tag in 2007 was dead — a victim of the infamous NVidia 8600 failure. I remember when such hardware failures meant going into my personal inventory of spare parts such as 3dfx Voodoo cards, ATX 350W power supplies, network cards, overclocked AMD Duron chips, and the like.  A desktop computer could be back up in no time.

Apple had the repair box to me on Tuesday and the computer arrived at their repair center on Wednesday.  It was back on my desk on Friday, all fixed.  Apple mistakenly charged me for the repair, but after a thirty minute call they credited me back the amount.

I like my MacBook Pro very much — better than any computer I’ve owned (all previous Windows machines).  It is not without its quirks, however. And now hardware failures.  Those entertaining “I’m a problem-prone PC, I’m an eager and trouble-free Mac” advertisements are products of Apple’s marketing department, not user surveys, and must be viewed with the proper dose of skepticism.

I am a little concerned that the replacement board with the same NVidia chip will just fail again after a certain number of heating/cooling cycles and that this MacBook Pro’s dependability, and indeed life expectancy, is in question.  I did have a week of downtime due to not having a backup computer.  That’s a reason not to sell your old laptop for $400 on eBay when upgrading!  That said, if I were suddenly back on my Dell Inspiron running CS2 on Windows, I might remember why I switched to Apple and OS X.

One thing I did learn about through this experience is Apple’s Target Disk mode.  If you hold down the T key while booting your Mac, it will function as an external firewire drive.  With both the monitor and external display not working due to the graphics card failure, I was still able to access the drive and pull my files off using Chris’ Windows PC and a program called MacDrive.