Writing well-behaved, efficient, AIR applications

The Adobe AIR platform makes it possible for many talented developers familiar with AJAX or Flash to build desktop applications. However, with great power comes great responsibility.

While an app running inside the browser can count on being short lived, it’s a different story with a desktop application. A desktop app may run for hours, if not days, at a time. Think of the many popular Twitter clients as an example.

As a desktop developer you have to give more thought to the resource usage of your application, such as the memory and the CPU, which is what I’m going to focus on here. The quantity of CPU cycles available on the machine is a resource shared by all the other apps (and system processes). Every CPU cycle your app use is a CPU cycle not available for something else. When the user is interacting directly with your application, using CPU cycles to present the most engaging experience possible is reasonable. However, when your application is in the background, your goal should be to reduce your CPU usage so that your app is as unobtrusive as possible and so that the application that the user is directly interacting with has more CPU cycles to use.

Keep in mind also that how much of the CPU is used affects the power consumption and therefore the battery life of laptops. Reduce the CPU usage of your app and save the planet.

What can you do? The first step is to measure. You can use some simple tools to get some rough data. For example, the Activity Monitor application on Mac OS, the top command line tool on Linux and the Task Manager on Windows.

Use these tools to watch out for the CPU usage of your application. Bring your application to the front, interact with it, and consider if the reported CPU usage is what you expect. While decoding high-def video you should expect that more of the CPU will be used than if your app is just doing some text editing.

Now, bring another app to the foreground. Watch out for the CPU usage of your app again. It should have dropped significantly. It’s OK for that number to jump back up from time to time (for example if you connect to a web service from time to time), but in general, lower is better.

As an aside, you can also use these tools to measure your app memory usage. Keep your app running for 24 hours, then measure the memory usage again. It should not have increased significantly, if at all.

How can you lower your CPU usage? Here are a few tips to consider.

1. Use the lowest framerate possible

The framerate of your application refers to how often the windows of your application are refreshed (the stage is redrawn on screen). With a framerate of 24, the content of your app is refreshed every 41ms (24 times per second). The framerate also defines how often the ENTER_FRAME event handler is invoked.

If you build a Flex-based application, the framerate by default will be 24. For many apps, that may be more than you really need. Try to lower the framerate as low as you can. Start with 7fps, then try to go lower. Once you’ve lowered the framerate see if there’s any interaction glitch while using your app. Watch out for animation and transitions in your app and make sure they’re still smooth.

To change the framerate of a Flex application, set the frameRate attribute in your mx:WindowedApplication tag.

For example:

    layout="absolute" frameRate="7"

If you are using Actionscript or Javascript, you can set the stage.frameRate property to the desired value.

In many cases a lower framerate has no discernible effect on the quality of animation or the interactivity of your application but will result in a decreased CPU usage. Notice also that the refresh of video is independent of the framerate of your application, so even if you’re playing video, try to lower your framerate.

2. Dynamically change the framerate to fit your application needs

In addition to lowering the framerate of your application overall, you should also consider changing the framerate depending on what your application needs at different point in time. For example, you may need a higher framerate when displaying an animation. However, this may happen infrequently and there’s no reason to set the framerate of your application to the maximum you will ever need. Instead, you can temporarily increase the framerate when you need it, then set it back to a lower value.

For example, you may need to increase the framerate (maybe to 12 or 24 frame per second) during a visual transition (a Flex state change), while responding to a dragging operation, while your window is being resized or the layout of your application changes, or while playing video.

On the other hand, consider lowering the framerate even further when your application goes in the background. If your app is in the background, that’s probably a good indication that the user is focussed on something else, and if it means pausing some animation, or having them occur with less fidelity, that may be a valuable tradeoff. You can set your framerate as low as 0.1 or even to

  1. Even with a 0 framerate, your app is not completely paused. It will still be sent events to react to, such as activation, window move and resize, mouse and keyboard, etc…

While in the background, if you have an ENTER_FRAME handler or some timers, you might want to consider unregistering them to further reduce your CPU usage. Restore them when your application is brought back to the foreground.

For example:

<span style="\"color:#696969;"
"="">// In your application initializer:</span> <span
style="\"color:#800000;" font-weight:bold;="" "="">this</span><span
style="\"color:#808030;" "="">.</span>addEventListener<span
style="\"color:#808030;" "="">(</span>AIREvent<span
style="\"color:#808030;" "="">.</span>APPLICATION_DEACTIVATE<span
style="\"color:#808030;" "="">,</span> applicationDeactivate<span
style="\"color:#808030;" "="">)</span><span style="\"color:#808030;"

<span style="\"color:#696969;" "="">// The application is being sent to
the background</span> <span style="\"color:#800000;" font-weight:bold;=""
"="">public</span> function applicationDeactivate(event<span
style="\"color:#808030;" "="">:</span>Event<span
style="\"color:#808030;" "="">)</span><span style="\"color:#808030;"
"="">:</span><span style="\"color:#800000;" font-weight:bold;=""
"="">void</span> <span style="\"color:#800080;" "="">{</span>
    <span style="\"color:#800000;" font-weight:bold;=""
    "="">this</span><span style="\"color:#808030;"
    "="">.</span>removeEventListener<span style="\"color:#808030;"
    "="">(</span>Event<span style="\"color:#808030;"
    "="">.</span>ENTER_FRAME<span style="\"color:#808030;" "="">,</span>
    enterFrame<span style="\"color:#808030;" "="">)</span><span
    style="\"color:#808030;" "="">;</span> stage<span
    style="\"color:#808030;" "="">.</span>frameRate <span
    style="\"color:#808030;" "="">=</span> <span
    style="\"color:#008000;" "="">0.1</span><span
    style="\"color:#808030;" "="">;</span>
<span style="\"color:#800080;" "="">}</span>

The above example uses Flex, but you can accomplish the same thing using the Event.DEACTIVATE event.

3. Only use Event.ENTER_FRAME handlers when necessary. Which is

almost never.

You could think that Event.ENTER_FRAME handlers provide a convenient mean to perform some repetitive action. However, you must be mindful that they are not called just “once in a while”, but with every single frame that gets drawn on screen. That’s a lot. In general, you should only use an Event.ENTER_FRAME handler if you need to do some processing whenever the display is refreshed. That should be exceedingly rare. There’s usually a better (more efficient) way than using an Event.ENTER_FRAME handler. In fact, the only case I can think of where you could justify using Event.ENTER_FRAME is to calculate the effective framerate of your application.

For example, if you need to track the mouse, register for Event.MOUSE_MOVE events instead. Those events will only get dispatched when the mouse is actually moved. You should consider register for Event.MOUSE_ENTER and Event.MOUSE_LEAVE if that’s really the only thing you need to know.

If you need to do some processing not related to display, for example some sound processing, use a Timer instead of Event.ENTER_FRAME. You can choose how frequently to invoke the timer based on the amount of processing you need. You may be able to use a lower framerate for the stage, and a higher frequency for the timer. This will save considerably on the amount of CPU used.

Note that for animations, you are often better off using a Timer as well. The Event.ENTER_FRAME handler may not be called at exactly the framerate you expect anyway. As a result, if you base an animation on how frequently this handler is called, stutter may be visible. Instead, base your animation on absolute time, which will allow you to vary the framerate as needed without affecting the animation:

<span style="\"color:#696969;"
"="">// Ease function to make the logo blink slowly</span> <span
style="\"color:#800000;" font-weight:bold;="" "="">public</span> function
ease(t<span style="\"color:#808030;" "="">:</span>Number<span
style="\"color:#808030;" "="">)</span><span style="\"color:#808030;"
"="">:</span> Number <span style="\"color:#800080;" "="">{</span>
    <span style="\"color:#800000;" font-weight:bold;="" "="">return</span>
    <span style="\"color:#808030;" "="">(</span>Math<span
    style="\"color:#808030;" "="">.</span><span
    style="\"color:#800000;" font-weight:bold;="" "="">sin</span><span
    style="\"color:#808030;" "="">(</span>Math<span
    style="\"color:#808030;" "="">.</span>PI <span
    style="\"color:#808030;" "="">*</span> <span
    style="\"color:#808030;" "="">(</span><span
    style="\"color:#008c00;" "="">2</span> <span
    style="\"color:#808030;" "="">*</span> t <span
    style="\"color:#808030;" "="">-</span> <span
    style="\"color:#008000;" "="">0.5</span><span
    style="\"color:#808030;" "="">)</span><span
    style="\"color:#808030;" "="">)</span> <span
    style="\"color:#808030;" "="">+</span> <span
    style="\"color:#008c00;" "="">1</span> <span
    style="\"color:#808030;" "="">)</span> <span
    style="\"color:#808030;" "="">/</span> <span
    style="\"color:#008c00;" "="">2</span><span
    style="\"color:#808030;" "="">;</span>
<span style="\"color:#800080;" "="">}</span>

<span style="\"color:#696969;" "="">// This handler is called anytime a
new frame is</span> <span style="\"color:#696969;" "="">// drawn. Use
sparingly.</span> <span style="\"color:#800000;" font-weight:bold;=""
"="">public</span> function enterFrame(event<span style="\"color:#808030;"
"="">:</span>Event<span style="\"color:#808030;" "="">)</span><span
style="\"color:#808030;" "="">:</span><span style="\"color:#800000;"
font-weight:bold;="" "="">void</span> <span style="\"color:#800080;"
       logo<span style="\"color:#808030;" "="">.</span>alpha <span
       style="\"color:#808030;" "="">=</span> <span
       style="\"color:#008000;" "="">0.2</span> <span
       style="\"color:#808030;" "="">+</span> ease<span
       style="\"color:#808030;" "="">(</span><span
       style="\"color:#800000;" font-weight:bold;="" "="">new</span>
       Date<span style="\"color:#808030;" "="">(</span><span
       style="\"color:#808030;" "="">)</span><span
       style="\"color:#808030;" "="">.</span>time <span
       style="\"color:#808030;" "="">/</span> <span
       style="\"color:#008c00;" "="">3000</span><span
       style="\"color:#808030;" "="">)</span><span
       style="\"color:#808030;" "="">;</span>
<span style="\"color:#800080;" "="">}</span>

Whether you use a Timer or a Event.ENTER_FRAME handler make sure you stop and unregister them as soon as you can. Even a handler that does “nothing” will be consuming some significant amount of CPU cycles. That actually also applies to any kind of other event handlers, such as Event.MOUSE_MOVE. As soon as you’re done with them, make sure to unregister them.

The same advice that applied to your overall application framerate also applies to the frequency of timers: try to use the lowest frequency you can get away with. Experiment to find out what is acceptable. Also, vary the frequency of timers as needed, for example reducing them when your application is deactivated.

4. Have as few Event.ENTER_FRAME handlers and Timers as possible

You often need to do periodic operations for a variety of reason. It would be tempting to have multiple Timers or multiple Event.ENTER_FRAME handlers. However, there’s a non insignificant overhead with each Timer (or handler). Instead, try to group all the periodic operation that you need to perform in as few Timers as possible.

All the operations that need to happen at the same frequency should be handled in the same timer. Try to have at most a couple of Timers, maybe one for animation with a frequency of 40ms and another to do “background” operations every 2000ms or so. Turn off those timers whenever you’re done (for example, turn off the animation timer when the animation is done, and start it again if you need a new animation).

Note also that setInterval and clearInterval are implemented using Timers, so don’t think they are any lighter weight.

And to conclude…


Here’s a sample app that demonstrates some of those techniques. The result is an app that uses less than 1% of CPU on Mac.