AIR:让Adobe AIR 程序离线运行

写在前面: 好久没有翻译东西了,一个原因是自己没什么时间,天天忙着背单词、看数学,每天上网的时候也忙着看新闻,有时候想要翻译一篇却找不到感觉,一点效率也没有,想当初看到一篇教程就复制到Word里,然后打开金山词霸和Google翻译,一段一段循序渐进,两个小时就搞定一篇,搞定之后的一段时间内就感觉人生充实,大好年华没有虚度。另外一个原因就是国外那些Flexer写的精品教程也少了很多,Kuwamoto好久不写了,Matt Chotin好久不写了,Peter Ent也不经常更新,不像以前,那是一篇接着一篇啊,我翻译起来也有劲,教程出来之后一般两天之内就能翻译并发表。

一开始想要翻译东西是觉得博客内容太空,自己写原创的没什么水平,翻译东西又省劲又可以增加博客的含金量,后来证实这样做确实招来了一些忠实读者,而且好像开始整个Flex界就我自己在翻译东西,我的博客一下子就成了独一无二的,极大满足了我的虚荣心,也是我持续翻译的动力。现在感觉翻译东西越来越像体力活了,刚开始的时候还可以提高点英语水平,后来就没什么可提高的了,想要提高英文水平的还是别走翻译这条路。其实国内也只有我这样的学生才会去翻译东西,翻译东西放在网上完全是无私奉献,而且对提高自己的技术水平作用不大,大家都在努力编码期待可以变成牛人挣大钱买房买车,翻译这种没有好处的事情鲜有人干。翻译这些文章带给我的最大好处是:我自己一步一步建立了一个资料库,关于Flex的中文资料我根本不用去其他站点找。还有就是,这些可能在找工作的时候有用,它们至少证明了我在大学的时候主动做了一些有意义的事情。

英文原文:Taking Adobe AIR Applications Offline
原文地址:http://labs.adobe.com/wiki/index.php/AIR:Articles:Taking_Apollo_Applications_Offline

原文作者:John C. Bland II (http://blogs.katapultmedia.com/jb2)

译者:Dreamer 此文未经同意,谢绝转载。

让Adobe AIR 程序离线运行

在我看来,AIR最好的功能之一就是可以创建可以在线运行也可以离线运行的应用程序。应用程序允许用户在离线状态下对他们的帐户,目录等进行修改,然后当恢复连接时将数据在线同步。有了这个功能,用户肯定更喜欢这个程序。

这篇文章中,我将会讲述如何在离线和在线状态下管理你的程序。我将把重点放在如何管理网络状态以及处理基本数据,而不是如何开发AIR程序和Flex 2.0.1程序。我将尽量使用简单的数据(XML)来演示在线获取数据和离线管理数据。

请记住所有的代码都是应用于AIR Alpha 1,而且这些代码是以我自己的方式写的,尽管我觉得这种方式很好,但是它并不是标准。你可以阅读完代码之后按照自己的方式去实现。

要求:

Flex Builder 2.0.1
Flash Player 9
AIR runtime

示例源码:

john_bland_sample_code.zip (1.83 MB)

检测网络状态

我将跳过Flex Builder来直接讨论代码。更多关于设定Flex Builder以及创建Flex程序的信息,请参考Flex开发者中心的关于IDE的章节

AIR的 Alpha 1版本提供了一个有用的事件(Event.NETWORK_CHANGE),当网络状态发生改变的时候它就会通知程序。这个事件并不会告诉你程序是否在线,它只是告诉你网络状态改变了:你可能处于离线状态、或在线状态、或登录到了一个VPN系统等。

让我们以捕获NETWORK_CHANGE 事件的基本代码开始。

[Code (AIROffline_Step1.mxml)]
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” creationComplete=”init()”>
<mx:Script>
<![CDATA[
private function init():void{
Shell.shell.addEventListener(Event.NETWORK_CHANGE, onNetworkChange);
}

private function onNetworkChange(event:Event):void{
trace(event);
}
]]>
</mx:Script>
</mx:WindowedApplication>
[/Code]

这段代码是检测网络的最短的代码。一旦程序的creationComplete 事件触发了init() 方法,init() 方法就会为Shell添加一个事件监听器并将触发器函数设定为onNetworkChange。在trace()语句那一行设置一个断点并以调试方式启动。当程序运行了以后断开网络连接,你会发现Flex Builder中程序运行到了断点处(看图1)。


图1.程序断点

确定在线状态

现在程序已经可以检测到网络状态的更改了。现在你需要知道当前的状态(在线或离线),我们将尝试加载数据,如果可以的话我们就处于在线状态,如果不能地话我们就假定已经离线了。现在让我们添加一点代码来检测网络状态。注意,未来了某个AIR版本有可能使我们不必写这些代码。

[Code (AIROffline_Step2.mxml)]
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml”
layout=”absolute” creationComplete=”init()”>
<mx:Script>
<![CDATA[
[Bindable]
private var isOnline:Boolean = false;
private var request:URLRequest = new URLRequest(”http://blogs.katapultmedia.com/jb2/_dev/onlineoffline/data/rooms.xml”);
private var requestLoader:URLLoader = new URLLoader();

private function init():void{
requestLoader.addEventListener(Event.COMPLETE, requestCompleteHandler);
requestLoader.addEventListener(IOErrorEvent.IO_ERROR, requestErrorHandler);
Shell.shell.addEventListener(Event.NETWORK_CHANGE, onNetworkChange);
requestLoader.load(request);
}

private function onNetworkChange(event:Event):void{
isOnline = !isOnline
trace(”Connected to Internet? ” + isOnline);
}

private function requestErrorHandler(event:IOErrorEvent):void{
isOnline = false;
}

private function requestCompleteHandler(event:Event):void{
isOnline = true;
}
]]>
</mx:Script>
</mx:WindowedApplication>
[/Code]

我们在代码中添加了一些东西。首先让我们简要地看一下流程。

1.程序初始化
2.象对网络连接发生改变时所作的一样,为数据的加载成功和加载失败添加一个事件监听器。
3.尝试加载XML文件(或者加载你需要的任何文件/数据)

现在程序可以确定连接状态了,我们的监听器函数将会相应地对isOnline变量做更改。注意onNetworkChange()方法很少将isOnline的值置反,处理这个的一种较好的方法就是同时访问一下服务器来确定连接状态。

现在NETWORK_CHANGE事件关联了很多东西,正如上面所说,当你从在线状态变成离线状态的时候会触发该事件,但是当你连接到VPN或其它状态改变的时候也会触发该事件。记住,服务器两次检查在线状态的时候可能会离线,这会使数据加载数据失败,导致程序显示为离线状态。希望下一个版本中的网络更新会改进这一过程。

现在NETWORK_CHANGE事件关联了很多东西,正如上面所说,当你从在线状态变成离线状态的时候会触发该事件,但是当你连接到VPN或其它状态改变的时候也会触发该事件。记住,服务器两次检查在线状态的时候可能会离线,这会使数据加载数据失败,导致程序显示为离线状态。希望下一个版本中的网络更新会改进这一过程。

以调试模式运行上面的代码,然后在连接状态和无连接状态观察控制台(Console)视图。


图2. 连接状态和无连接状态下的控制台视图

创建一个基于状态的程序

现在我们已经知道了连接状态而且仅仅需要我们的程序相应地作出响应。现在你要做的是将currentState属性绑定到isOnline变量来获得所需的效果或流程。

改变程序的状态

在这里我假定你很熟悉Flex 2中的绑定,所以对这些基础就一带而过了。这里我们要做的是根据isOnline的值切换状态。

[Code (snippet; AIROffline_Step3.mxml)]
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml”
layout=”absolute” creationComplete=”init()”
currentState=”{isOnline ? ‘Online’ : ‘Offline’}”>
[/Code]

你需要注意的是currentState 那一行。它告诉我们在线或离线状态的改变是基于isOnline变量的。很简单,是不是?现在让我们来创建各个状态。

[Code (snippet; AIROffline_Step3.mxml)]
<mx:states>
<mx:State name=”Online”>
<mx:SetProperty name=”status” value=”Online”/>
</mx:State>
<mx:State name=”Offline”>
<mx:SetProperty name=”status” value=”Offline”/>
</mx:State>
</mx:states>
[/Code]

在每个状态中,我们仅仅改变了WindowedApplication.status 的值来显示当前的状态。对于习惯图形化界面表示的你来说,可能想让我根据状态用灰色或全彩色来表示网络连接,好吧,我将添加一个状态条来满足你的图形化要求。

接下来让我们处理数据。

管理数据

我们已经可以知道当前的状态是什么了,现在需要做的就是处理数据。首先让我们看一下需要做的事情以及想要的结果。

•如果是离线状态,加载本地数据文件。
•如果是在线状态,加载线上的数据文件并将其保存在本地,为将来离线加载做准备。(如果加载数据失败,就回过头来加载本地数据文件)
•如果网络状态从离线变成在线,就加载线上数据并重复上一步。
•结果:程序自动更新数据(通过绑定),而且不管状态如何数据一直是可用的。

大部分的代码都和上面的差不多,这里我只给出一些代码片断以便你更清楚地看到是哪里做了更改。

[Code (snippet; Script block; AIROffline_Step4.mxml)]
import flash.filesystem.*;
private var localFile:File = File.appStorageDirectory.resolve(”AIROffline/rooms.xml”);
private var localFileStream:FileStream;
…[other code]
private function init():void{
requestLoader.addEventListener(Event.COMPLETE, requestCompleteHandler);
requestLoader.addEventListener(IOErrorEvent.IO_ERROR, requestErrorHandler);

Shell.shell.addEventListener(Event.NETWORK_CHANGE, onNetworkChange);

//create/open local file
localFileStream = new FileStream();
localFileStream.open(localFile, FileMode.Update);
localFileStream.close();

loadData();
}
[/Code]

对于文件管理来说,AIR for Adobe Flex Developers Pocket Guide是一本很好的参考书,所以这里我不会介绍太多的细节。我们在变量声明部分所做的就是创建了一个FileStream对象并将其赋给了localFile。如果文件存在,FileMode.Update将会它,如果文件不存在,就创建一个空白文件。然后文件就会被关闭,因为在这里我们想要做的就是要确保这个XML文件存在。

[Code (snippet; AIROffline_Step4.mxml)]
//Cleanup Methods
private function applicationClosingHandler(event:*):void{
localFileStream.close();
}

//Data Methods
private function loadData():void{
requestLoader.load(request);
}

private function readLocalFile():void{
localFileStream.open(localFile, FileMode.READ);
roomsXML = XML(localFileStream.readUTFBytes(localFileStream.bytesAvailable));
localFileStream.close();
}

private function saveDataLocally():void{
localFileStream.open(localFile, FileMode.WRITE);
localFileStream.writeUTFBytes(’<?xml version=”1.0″ encoding=”utf-8″?>\n’+roomsXML.toXMLString());
localFileStream.close();
}

//Connection methods
private function onNetworkChange(event:Event):void{
isOnline = !isOnline
if(isOnline){
loadData();
}
}

private function requestErrorHandler(event:IOErrorEvent):void{
isOnline = false;
//Get data from local file
readLocalFile();
}

private function requestCompleteHandler(event:Event):void{
isOnline = true;
roomsXML = XML(requestLoader.data);
//Write data locally
saveDataLocally();
}
[/Code]

applicationClosingHandler()函数只是为了确保在程序关闭的时候FileStream也会随之关闭。上面的大部分代码想必你已经很熟悉了,这里注意readLocalFile() 和saveDataLocally() 两个函数,这两个函数是用来管理本地文件或数据的,它们的工作流程如下:

如果 isOnline == true
# 在初始数据加载之后调用requestCompleteHandler()
# 数据被保存在roomsXML变量中,由于进行了绑定程序的显示会自动更新。
# 调用saveDataLocally()
# 将roomsXML中的数据保存到FileStream

如果 isOffline == false
# 调用requestErrorHandler()
# 调用readLocalFile()
# 将本地roomsXML文件中的内容保存在roomsXML中

处于在线状态的时候我们只需要刷新本地数据,这样的话只有在isOnline为true的时候onNetworkChange()才需要再次调用loadData()。一旦加载了数据就重复上面“如果isOnline == true”的步骤,将数据保存到本地并重新设置roomsXML的值,然后更新界面显示。

我在显示中多添加了几个数据项,这样就可以看到数据的改变。

[Code]
<mx:VBox width=”100%” height=”100%”>
<mx:Text text=”{roomsXML.Room.length()} Rooms Available”/>
<mx:TextArea id=”RoomsList” width=”100%” height=”100%” text=”{roomsXML.toXMLString()}” selectable=”false” editable=”false”/>
</mx:VBox>
[/Code]

这个时候,下面所示的XML数据是线上的。图3显示了实用这个XML数据程序运行后的结果。

[Code (data/rooms.xml)]
<?xml version=”1.0″ encoding=”utf-8″?>
<Rooms>
<Room name=”Room 1″ />
<Room name=”Room 2″ />
<Room name=”Room 3″ />
<Room name=”Room 4″ />
<Room name=”Room 5″ />
<Room name=”Room 6″ />
<Room name=”Room 7″ />
<Room name=”Room 8″ />
<Room name=”Room 9″ />
<Room name=”Room 10″ />
</Rooms>
[/Code]


图 3. 使用这个XML数据运行程序

现在,我将断开网络连接,关闭程序然后重新运行它。(看图4)


图 4.重新运行程序

OK,没有什么两样对吧?唯一发生改变的是底部所示的网络状态,现在我将关闭程序,然后在更新线上XML数据以后断开网络连接。

下面是新的XML(只是复制了一下Room节点)。

[Code (data/rooms.xml)]
<?xml version=”1.0″ encoding=”utf-8″?>
<Rooms>
<Room name=”Room 1″/>
<Room name=”Room 2″/>
<Room name=”Room 3″/>
<Room name=”Room 4″/>
<Room name=”Room 5″/>
<Room name=”Room 6″/>
<Room name=”Room 7″/>
<Room name=”Room 8″/>
<Room name=”Room 9″/>
<Room name=”Room 10″/>
<Room name=”Room 1″/>
<Room name=”Room 2″/>
<Room name=”Room 3″/>
<Room name=”Room 4″/>
<Room name=”Room 5″/>
<Room name=”Room 6″/>
<Room name=”Room 7″/>
<Room name=”Room 8″/>
<Room name=”Room 9″/>
<Room name=”Room 10″/>
<Room name=”Room 1″/>
<Room name=”Room 2″/>
<Room name=”Room 3″/>
<Room name=”Room 4″/>
<Room name=”Room 5″/>
<Room name=”Room 6″/>
<Room name=”Room 7″/>
<Room name=”Room 8″/>
<Room name=”Room 9″/>
<Room name=”Room 10″/>
</Rooms>
[/Code]

现在,我将在离线状态下重新运行程序,加载完本地数据的结果和图4中所示一样。接下来你将看到一些有趣的事情。我将保持程序的运行,然后连接网络。线上的数据会被下载下来并保存在本地(看图5)。


图 5. 线上数据被下载并保存在本地

好极了!注意程序并没有被关闭,它一直在运行,我只是连接了网络而已。

最后,我将断开网络然后重新运行程序。运行结果和图5中所示一样,只是状态变成了离线。

最终代码:

[Code]
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:WindowedApplication xmlns:mx=”http://www.adobe.com/2006/mxml”
layout=”absolute” creationComplete=”init()”
currentState=”{isOnline ? ‘Online’ : ‘Offline’}”
closing=”applicationClosingHandler(event)”>
<mx:Script>
<![CDATA[
import flash.filesystem.*;

private var localFile:File = File.appStorageDirectory.resolve("AIROffline/rooms.xml");
private var localFileStream:FileStream;

[Bindable]
private var isOnline:Boolean = false;
private var request:URLRequest = new URLRequest(”http://blogs.katapultmedia.com/jb2/_dev/onlineoffline/data/rooms.xml”);
private var requestLoader:URLLoader = new URLLoader();
[Bindable]
private var roomsXML:XML = new XML();

private function init():void{
requestLoader.addEventListener(Event.COMPLETE, requestCompleteHandler);
requestLoader.addEventListener(IOErrorEvent.IO_ERROR, requestErrorHandler);

Shell.shell.addEventListener(Event.NETWORK_CHANGE, onNetworkChange);

//create/open local file
localFileStream = new FileStream();
localFileStream.open(localFile, FileMode.Update);
localFileStream.close();

loadData();
}

//Cleanup Methods
private function applicationClosingHandler(event:*):void{
localFileStream.close();
}

//Data Methods
private function loadData():void{
requestLoader.load(request);
}

private function readLocalFile():void{
localFileStream.open(localFile, FileMode.READ);
roomsXML = XML(localFileStream.readUTFBytes(localFileStream.bytesAvailable));
localFileStream.close();
}

private function saveDataLocally():void{
localFileStream.open(localFile, FileMode.WRITE);
localFileStream.writeUTFBytes(’<?xml version=”1.0″ encoding=”utf-8″?>\n’+roomsXML.toXMLString());
localFileStream.close();
}

//Connection methods
private function onNetworkChange(event:Event):void{
isOnline = !isOnline
if(isOnline){
loadData();
}
}

private function requestErrorHandler(event:IOErrorEvent):void{
isOnline = false;
//Get data from local file
readLocalFile();
}

private function requestCompleteHandler(event:Event):void{
isOnline = true;
roomsXML = XML(requestLoader.data);
//Write data locally
saveDataLocally();
}
]]>
</mx:Script>
<mx:states>
<mx:State name=”Online”>
<mx:SetProperty name=”status” value=”Online”/>
</mx:State>
<mx:State name=”Offline”>
<mx:SetProperty name=”status” value=”Offline”/>
</mx:State>
</mx:states>

<mx:VBox width=”100%” height=”100%”>
<mx:Text text=”{roomsXML.Room.length()} Rooms Available”/>
<mx:TextArea id=”RoomsList” width=”100%” height=”100%” text=”{roomsXML.toXMLString()}” selectable=”false” editable=”false”/>
</mx:VBox>
</mx:WindowedApplication>
[/Code]



本文链接: http://www.zhuoqun.net/html/y2007/628.html 转载请注明出处,谢谢。

TrackBack引用地址:http://www.zhuoqun.net/html/y2007/628.html/trackback

相关日志


Posted in AIR&Apollo, 技术.

1条评论

  • At 2007.12.28 14:51, airia CHINA said:

    很高兴来到你的博客 非常希望你能上WWW.AIRIA.CN 与大家交流AIR方面的问题。

    (Required)
    (Required, will not be published)