Mobile Application Development

Main menu

Post navigation

Custom AIR updater interface using ApplicationUpdater

I have updated the project to work with Flash Builder 4, you can get the code at this post.

Original Post:

To tell you the truth, I never gave the ApplicationUpdaterUI much thought in terms of memory use before this post. I just thought it was a great way to add a complete update system to your AIR applications. I had not thought it could be so memory intensive until a recent conversation with Jesse Warden on Twitter:

* Please note that Jesse is dude that looks about 17 and I am the older more distinguished gentleman.

Problem with ApplicationUpdaterUI

Indeed, that “mofo eats mad RAM” is putting it lightly. It turns out that the ApplicationUpdaterUI consumes about 14MB of RAM within the application. Furthermore, for the ApplicationUpdaterUI to even check for available updates, it must loads the entire UI in memory even if not displayed. So just by using the framework, you end adding 14MB to your application that won’t be garbage collected. Granted, in most cases 14MB of RAM is not that big of deal. However, for smaller applications you might want to consider an alternative approach.

There happens to another class file called ApplicationUpdater. This class basically gives you all the benefits of the ApplicationUpdaterUI framework without any of the visible elements. This means you have to build your own user interface. This might actually be a benefit in the long run as you may want to customize your application updater to match your application. The other benefit is that you can use the ApplicationUpdater class to check for updates without loading any user interface. The interface only needs to be loaded if there is an actual update available.

Custom Updater Interface with ApplicationUpdater

In building my own custom updater interface, I thought it might be nice to have the same look and feel of the ApplicationUpdaterUI along with some of the more polite features (like postpone update until restart). Building your own updater using the ApplicationUpdater turns out to be a little more involved than I had originally thought. However, when you start attacking a problem you persevere until the end, no matter what the pain.

I did find some available information in the Adobe AIR 1.5 Cookbook, which has a basic custom updater example. This helped with some of the basic stuff, but I still needed a little more detail to make something similar to the ApplicationUpdaterUI. I found another good example by Jens Krause, but this example is for Flex 4. I needed something that would work for Flex 3. So here is what I built based on these examples:

The application consists of one class file and one MXML dialog for displaying all the user interface elements. I borrowed some of the icons from the ApplicationUpdaterUI which is available in the AIR SDK (I hope Adobe doesn’t mind). The update dialog UI is only loaded when needed, all checks for application updates happen using the ApplicationUpdater. The update dialog is only displayed when the user wants to manually update or the periodic update check finds a new application update.

The Good and the Bad

The good news is the updater only consumes about 2MB of RAM when you load the update dialog box, part of that is the ApplicationUpdater itself running in the background. The other good news is that you can yank out or customize any part of the this example to meet your own needs. The bad news is that even though I tried my best to fully remove the update dialog and have garbage collection clean it up, it doesn’t fully unload from memory (go figure). I don’t feel this is a huge issue as the application is most likely restarted after you install an update. Below is the code followed by a zip file containing the full application.

I packed up the Flex project. You would use this basically the same as you would use the ApplicationUpdaterUI framework. You need to create an update version of your AIR file and place it and update.xm file on a server. You can test it from the local IDE if you replace the server url with “app:/”. Just remember you can not actually update an AIR application from the IDE, it has to be installed and running on your machine to update, and the update files have to be accessible just like when you use the ApplicationUpdaterUI.

The code is free to use, hose, rip apart, just post back any fixes or enhancements you make.

Don’t forget to add a closing event handler to the dialog box that calls the destory() function. This was not in the original code and there needs to be a way to handle users clicking on the close box of the Window for the update dialog user interface. I also added a change that if you don’t want to show the check for update now option, the “no update available” dialog does not appear either, here is the updated code for statusUpdate function in UpdateManager.as (not in the downloaded zip):

I have a critical situation in which I need to check for whether the user has pressed INSTALL NOW or INSTALL LATER (essentially, my app closes the main window to open a new window, and it closes the main window and then the update occurs, I get an error about damaged AIR file — go figure).

Using ApplicationUpdaterUI, I don’t get a chance to listen to the events from that INSTALL NOW/INSTALL LATER dialog, as far as I can tell. If I go your route, I can easily find out.

Damaged AIR file usually means that the certificates are out of sync or the version you are installing is not the same as what you had in your update.xml file. At least, that has been my experience :). Also check that users (well, its probably you) have permissions to install the application, etc. The last resort is, you have to totally uninstall all remnants of your application from the system, even the stored application directory. Sometimes things get corrupted and trashed during testing. You can follow these steps to uninstall Adobe AIR completely:

If there is no update, then event.available is false and the conditional falls to the statement above. However, if showCheckState is true, when you issue createDialog(), it will fail because createDialog() has the following check:

[cc]if(!updaterDialog) { …[/cc]

Therefore, if the showCheckState is true, it will ask the user if they want to check for updates, but if there is no update available (event.available == false), it will try to createDialog() which will not touch the dialog (and hence not say that no update is available, nor destroy the check for update dialog). Please correct me if I’m wrong about this!

Is this because the air sdk 1.5 -version was the first version where they implemented the ApplicationUpdater / ApplicationUpdaterUI ?
Or is this just a formality of Adobe?

Logically I would think namespace ending on 1.0 is air framework 1.0, namespace ending on 1.5 is air framework 1.5 etc .etc.
But might be wrong here. Just was wondering why.

Another question I have, would it be “wise” to pass the base-url of the configuration.xml to the appUpdater when an Update is available? In that way you could (if desirable) change the download-location for the update.

That would be passing the baseURL to the constructor of the updateManager.
[as]
public function UpdateManager(showCheckState:Boolean = true, initializeCheckNow:Boolean = false, baseUrl:String = null)
{
this.baseURL = baseURL;
…
}

Man, this was working great for me until the final release of the Flex 4 SDK, and then all of a sudden my updater dialog started coming up empty – you could just barely see a sliver of the black dialog background in the lower right of the window. After much hair-pulling and gnashing of teeth, I found that just moving the open() up higher in the createDialog function, right after the constructor, fixed it.

I have been searching for a while with no answer yet about recovering from an error immediately. When a download error occurs, 404 in my case, i need to recover immediately and allow the user to try again as there could be instances where the server will respond with 404 or some other response which would kick off the DownloadErrorEvent and allow the user to try the download again. I tried running checkNow() immediately afterwards but it requires that the application updater be in the READY state, so i then use cancelUpdate() to get into the READY state, but checkNow() or checkForUpdate() still wont fire and advance the state to AVAILABLE once it cant reach the server and read the update descriptor again. Any advice on how to recover from download errors and allow the user to retry?

I am trying to figure out if it’s possible to use the Updater class to only push updates to parts of the app, rather than the entire app. I have an elearning app where I’ll be including new lessons each week, but wouldn’t want the user to have to re-download the entire app and all previous lessons just to get the new content. I know I don’t have to use the Updater class but it seems like it would be an easy way to get the updates done. Have you seen any custom implementations that don’t require the user to update the entire Air app?

What I might suggest is doing something similar to the updater, read an xml file from a remote server on predetermined interval, but just download the new lessons and place them into the storage directory for the application. You can then “reload” the applications into the application. Treat your applications as separate individual SWF files packed for each lesson or a separate XML structure that is used to build the lesson from assets downloaded along with the new lesson XML. Just make your application more modular and only download the new modules needed for the new lessons. You could even do the uploading in the background and make new content available in a navigation list the next time the application is launched.

Seems to me you just need to build system for checking if there is a new newsletter_chooser.xml file and download that to the assets folder of the application. AIR can download any file and place it any place on the computer. So you could do a check on startup that looks to see if there is a new newsletter_chooser.xml file available in a certain location, if so, pause the application to download this file and save it locally first, then refresh the application. It’s exactly the same principle as the Updater, but you are just downloading a single xml file. You could even do something like always download the newsletter_chooser.xml if user has internet connection before application initializes completely. If they don’t have a connection, then it just uses the current xml already stored with the application in the assets directory.