28 February 2011
随着 Adobe AIR SDK 2的发布,开发人员能够将自己的应用程序打包至本地安装程序文件中。 之后,这些安装程序文件能够在不同的平台(例如 Windows (EXE)、Mac OS X (DMG) 和Linux)上启动,这些平台具有支持从 deb或 rpm 文件进行安装的发布功能。 该功能能够用于各种情景,但它的主要目的是引入NativeProcess API,该API能够为开发人员提供从AIR应用程序执行本地代码的能力。 该新AIR应用程序安装过程可以确保应用程序能够在相应的目标平台上正常运行。
但很不幸的是与AIR SDK一起提供的Update Framework不支持对被打包至本地安装程序中的AIR应用程序进行更新。 不过,好消息是 NativeProcess API能够便捷地帮助建立你自己的更新机制。
本文将逐步引导你了解如何为 Windows 平台创建你自己的自定义更新机制的过程。 该机制的逻辑将被封装到 NativeUpdater 类中,该类具有一个用于触发更新过程的单一全局函数updateApplication 。 如需获得完整的源代码,下载与本文一起提供的范例文件。 关于如何更新 Mac OS X平台的安装程序的详细信息,请参阅本文的结尾部分章节。
更新机制必须执行的第一步是下载更新描述符文件以便核查是否存在任何更新。 更新描述符文件可以是你在标准的AIR Update Framework中使用的相同文件。 这里的假设前提是必须使用 AIR 2.5 SDK创建应用程序,而AIR 2.5 SDK包含新的命名空间标签 <versionNumber /> (以前为 <version />);这对更新描述符和应用程序描述符文件均适用。 你可以使用的更新描述符文件应该与下列代码相似:
<?xml version="1.0" encoding="utf-8"?>
<update xmlns="http://ns.adobe.com/air/framework/update/description/2.5">
<versionNumber>0.9.1</versionNumber>
<url>app:/remoteFolder/NativeUpdater.exe</url>
<description>This is a new version of NativeUpdater application.</description>
</update>
注意,这里的 url 标签应该指向放置你的本地安装程序的应用程序目录(很可能放置于你的http服务器)。
downloadUpdateDescriptor 函数将使用URLLoader类以便发起下载过程。 URLLoader 实例将具有两个定义的事件处理程序,以便处理全部的错误事件和IO错误事件。
protected function downloadUpdateDescriptor():void
{
var updateDescLoader:URLLoader = new URLLoader;
updateDescLoader.addEventListener(Event.COMPLETE, updateDescLoader_completeHandler);
updateDescLoader.addEventListener(IOErrorEvent.IO_ERROR, updateDescLoader_ioErrorHandler);
updateDescLoader.load(new URLRequest(UPDATE_DESCRIPTOR_URL));
}
下一步, updateDescLoader_completeHandler 函数读取下载的更新描述符 XML文件,并且将当前的应用程序版本与更新版本进行比较。 如果这些值不一致,则它调用 downloadUpdate 函数,将本地安装程序文件的URL地址传递给它。
protected function updateDescLoader_completeHandler(event:Event):void
{
var loader:URLLoader = URLLoader(event.currentTarget);
// Closing update descriptor loader
closeUpdateDescLoader(loader);
// Getting update descriptor XML from loaded data
var updateDescriptor:XML = XML(loader.data);
// Getting default namespace of update descriptor
var udns:Namespace = updateDescriptor.namespace();
// Getting application descriptor XML
var applicationDescriptor:XML = NativeApplication.nativeApplication.applicationDescriptor;
// Getting default namespace of application descriptor
var adns:Namespace = applicationDescriptor.namespace();
// Getting versionNumber from update descriptor
var updateVersion:String = updateDescriptor.udns::versionNumber.toString();
// Getting versionNumber from application descriptor
var currentVersion:String = applicationDescriptor.adns::versionNumber.toString();
// Comparing current version with update version
if (currentVersion != updateVersion)
{
// Getting update url
var updateUrl:String = updateDescriptor.udns::url.toString();
// Downloading update file
downloadUpdate(updateUrl);
}
}
在这一步骤中,更新机制必须从远端下载更新文件。 在开始下载文件之前,更新机制还必须解析下载文件的名称。 通过解析URL地址的最后片段并且利用位于临时目录的该片段的名称创建一个指向该文件的参考,可以实现上述任务。
现在,启动真正的下载过程;你可以使用URLStream类,该类针对这类场景非常有效。 你也可以使用URLLoader,但它需要在将整个应用程序文件写入硬盘驱动器之前在内存中对它进行缓存,而这对于大应用程序包来说不是最佳的方法。
protected function downloadUpdate(updateUrl:String):void
{
// Parsing file name out of the download url
var fileName:String = updateUrl.substr(updateUrl.lastIndexOf("/") + 1);
// Creating new file ref in temp directory
updateFile = File.createTempDirectory().resolvePath(fileName);
// Using URLStream to download update file
urlStream = new URLStream;
urlStream.addEventListener(Event.OPEN, urlStream_openHandler);
urlStream.addEventListener(ProgressEvent.PROGRESS, urlStream_progressHandler);
urlStream.addEventListener(Event.COMPLETE, urlStream_completeHandler);
urlStream.addEventListener(IOErrorEvent.IO_ERROR, urlStream_ioErrorHandler);
urlStream.load(new URLRequest(updateUrl));
}
当URLStream被打开时, urlStream_openHandler 函数将被调用。 在该函数中,可以创建FileStream类的实例;该类可以用于将下载的字节写入本地更新文件中。
protected function urlStream_openHandler(event:Event):void
{
// Creating new FileStream to write downloaded bytes into
fileStream = new FileStream;
fileStream.open(updateFile, FileMode.WRITE);
}
更新文件字节是以批量的形式下载的,这就是为什么必须处理进展事件以便将这些字节写入本地文件。 urlStream_progressHandler 函数可以负责实现这一任务并且它使用 ByteArray 从 URLStream读取下载的字节并且将它们写入 FileStream实例。
protected function urlStream_progressHandler(event:ProgressEvent):void
{
// ByteArray with loaded bytes
var loadedBytes:ByteArray = new ByteArray;
// Reading loaded bytes
urlStream.readBytes(loadedBytes);
// Writing loaded bytes into the FileStream
fileStream.writeBytes(loadedBytes);
}
当URLStream 完成下载更新文件字节之后,URLStream和 FileStream 均将被关闭。 下一步,可以调用installUpdate函数 。
protected function urlStream_completeHandler(event:Event):void
{
// Closing URLStream and FileStream
closeStreams();
// Installing update
installUpdate();
}
最后需要完成的步骤实际上是运行下载的更新文件。 为了实现这一目的,你可以使用 NativeProcess API。 记住,为了使用这一新的API,你的应用程序描述符(*-app.xml) 文件需要将supportedProfiles 标签设置为 extendedDesktop 值,参见下面代码片段:
<supportedProfiles>extendedDesktop</supportedProfiles>
首先,installUpdate 函数创建一个 NativeProcessStartupInfo类的实例并且将executable 属性设置为更新文件的引用。 其次,它使用NativeProcess类执行下载的更新文件。 最后,可以关闭应用程序以便让安装程序运行。
protected function installUpdate():void
{
// Running the installer using NativeProcess API
var info:NativeProcessStartupInfo = new NativeProcessStartupInfo;
info.executable = updateFile;
var process:NativeProcess = new NativeProcess;
process.start(info);
// Exit application for the installer to be able to proceed
NativeApplication.nativeApplication.exit();
}
当应用程序退出并且更新文件启动时,用户应该看到由 AIR运行时显示的标准更新窗口 (参见图1)。 点击 Replace 按钮可以启动更新过程;在更新过程完成之后,应用程序将自动重新启动。
在使用 Mac OS X系统的情形下,该过程将变得复杂一点。 首先,你必须安装下载的DMG文件。 这可以通过使用命令行util 应用程序 hdiutil来实现,该应用程序通常位于 /usr/bin/hdiutil 。应该使用 NativeProcess API执行hdiutil,并且它具有三个传递参数:attach、-plist 和下载更新文件的路径。 作为响应,hdiutil 将传递plist XML内容,该内容可以利用ProgressEvent.STANDARD_OUTPUT_DATA 事件处理程序进行读取。 从返回的 XML数据中,你可以提取至安装 DMG 文件的路径,它可以作为mount-point 关键节点的值进行传递。 最后一步是执行安装程序文件,该程序位于安装的DMG文件的Contents/MacOS 目录下。 安装程序能够使用上述的NativeProcess API 以在Windows平台上相同的方式执行。
当更新利用本地安装程序打包的应用程序时的另一个选项是使用我的 NativeApplicationUpdater库,它可以从 Google Code*网站获得。 使用该库的优点是它能够隐匿各种OS之间的差异。 你可以在我的博客*中找到如何为该库创建自定义UI的简短教程。 你也可以观看下面的视频以便看看它是如何工作的:
如果你希望建立你自己的更新器并且还希望它能够支持其它 OS,我推荐你通读 NativeApplicationUpdater 源代码以便了解这些平台是如何被处理的。
Tutorials and samples |
AIR blogs |
More |
AIR Cookbooks |
More |