发人深省的一段话
初从文,三年不中;后习武,校场发一矢,中鼓吏,逐之出;遂学医,有所成。 自撰一良方,服之,卒。
(链接 AIR:Articles:Taking Apollo Applications Offline)
作者 John C. Bland II (http://www.johncblandii.com)
个人认为,RIA 的最大特点是有能力创建一个在线和离线都可以运行的应用程序。这个应用程序允许用户更改他们的帐号,内容等。当连接恢复到在线的时候,便异步更新数据。用户更喜欢这样的应用程序。
通过这篇文章,我将处理在离线和在线状态下如何去管你的应用系统。在开发 RIA 和 Flex 2.0.1 应用系统的重点将放在管理网络状态和处理基本数据。不论是在线的数据接受还是离线的管理我将都采用这个简易的数据数据类型(XML)。
请记住所有的带来都来自 AIR Alpha1 和我自己原创的东西而并没有相关的 “书” 涉及到这些,尽管我认为这并不是什么伟大的创作。你可以从这里学习并融入你自己的想法。
内容目录:
为了能掌握这篇文章更多,你需要如下的软件和文件:
示例文件:john_bland_sample_code.zip (1.83 MB)
先决条件:Flex 3,MXML, 和 ActionScript 3.0 的知识
我将跳过 Flex Builder 和代码的深入。关于设置 Flex Builder 和创建 Flex 应用程序的更多的信息,可以访问 Flex Developer Center 的 IDEs section。
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 () 方法,在这个方法里添加一个事件监听器,命名这个监听函数为 onNetworkChange。在的 trace() 语句这一行设置一个断点,由此来进行调试。当这个应用程序被开启,断开你的网络链接,你将看到你的 Flex Builder 将应用程序停止到这个断点(如图一)。
图 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]
我们在代码里添加了一些东西。首先让我们简略的浏览下这个流程
这里指出应用程序被设置并准备去决定连接的状态。从而我们的监听函数将改变 isOnline 变量的状态。注意 onNetworkChange() 方法仅仅改变 isOnline 对应的值。更好的方法处理这使用延迟的服务器请求直到确保连接状态。
关注当前 NETWORK_CHANGE 事件有多个地方。当你从在线切换到离线的时候这个事件被调用,如上所述,当你连接入 VPN 的时候同样被触发。记住服务器是使用双击在线状态来切换到离线,这样会使数据的装载失败,从而使应用程序显示为离线状态。查找网络更新后再完成这个操作。
为了这篇文章,我们将提供一个最佳情况(服务器运行正常并没有其他类型的连接)但是并不能用这样的方法来研究应用程序。将来的 RIA 解决方案,提供更好的网络支持,这才是可行的,一定要重写 / 更新你的代码来更准确的处理前面 “最佳情况”(如上所述)。
在调试模式下运行上面的代码,当你连接和断开你的网络连接的时候,在 Console 视图中看到如下的情况(如图 2)。
图 2. 当你连接和断开连接时候的输出视图
下一步就是在连接状态下创建一个应用程序。
我们现在知道连接状态仅仅需要和应用程序对应而已。你可以把你应用程序的 currentState 属性绑定到 isOnline 变量以达到想要的效果。
我假设你已经很熟悉 Flex 2 因此我会在这个基础上扩展。我们将实现通过基于 isOnlline 的真假来选择应用程序的状态。
[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 变量改变 Online 或 Off line 状态。非常简单,是不是?现在让我们创建 states。
[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]
states 中的每个状态仅仅在改变 WindowedApplication.status 的值的时候展现当前的状态。你想用图形的方式告诉用户,你或许希望我能展现一些真正漂亮的网络连接的图形表示,基于状态使用灰色和全彩色来表示。一个你所能得到的状态条的改变伴随我的图形的改变。
那么接下来我们怎么做?我很高兴你会这样问。让我们处理这些数据。
我们有自己的连接状态能告诉我们当前的状态。现在我们需要处理数据。我们将再次调用 onNetworkChange() 函数和其他一些东西。让我们首先来看先我们的目标和所要得到的结果。
大部分代码已经提过了,因此这里我只显示一小部分,这样我们看到代码更改的部分更清楚。
[Code (snippet; Script block; AIROffline_Step4.mxml)] import flash.filesystem.*; private var localFile:File = File.applicationStorageDirectory.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 对象在创建指向本地文件的时候,是在定义这个变量的时候决定的。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"?> ' +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() 函数。这些函数被用来管理本地的文件 / 数据。这些步骤如下:
If isOnline == true
# requestCompleteHandler() 的调用发生在初始数据装载之后.
# 在 roomsXML 变量中保存数据;应用程序根据绑定自动更新显示
# saveDataLocally() 被调用
## 保存 roomsXML 中的数据到 FileStream 中去
If isOffline == false
# requestErrorHandler() 被调用
# readLocalFile() 被调用
## 设置 roomsXML 的内容为本地 roomsXML 文件中的内容
只有 isOnline 是真的时候,当 onNetworkChange() 中是在线的时候我们必须再次调用 loadData () 更新本地数据。当数据装载我们重复以上这些步骤 "If 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 让你看到运行应用程序后这个数据的结果。
[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. 重新打开应用程序
好的,这里没有任何变化,是么?只有一个地方发生了变化那就是 status,但是我在关闭应用程序然后在更新在线的 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. 在线文件装载并保存到本地
太好了!记住应用程序从来没有关闭。我一直保持打开仅仅重新连接下而已。如果
Sweet! Keep in mind the application never closed. I kept it open and just reconnected. If this doesn't wet your whistle of excitement, I don't know what will!
To finish the cycle, I'm going to disconnect from my network then open the application again. The result is Figure 5 with a state of Offline. Yummy!
OK: Everyone at the same time, "AIR IS SWEET!" Let's look at what we covered today.
From the ground up we built a quick application that monitored network status and responded to the status accordingly. We also setup a data sync so the user would always have the latest online data locally without having to request it. Our data was simple XML but you can see how this process could be utilized to store specific user information offline application use while in a disconnected state. The possibilities of how your application functions offline is 100% up to you. AIR gives you the tools and leaves it to you to be creative.
Again, keep in mind this is with AIR Alpha 1. I used some, what will become, unnecessary techniques. Keep an eye out for future releases of AIR. For now, enjoy Alpha 1!
I hope this article was helpful. If you have any questions and/or concerns, feel free to email me (mailto:[email protected]) and I'll try to respond within 24 hours. My apologies in advance for the spam trap.
Download AIR (when available), game-plan your application, and get rocking! Let AIR guide you on a path to a highly usable online/offline application.
Final code:
[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"?> ' +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]
John C. Bland II is CEO and Chief Developer for Katapult Media, Inc. (http://www.katapultmedia.com), an Arizona based software and web development company. Along with running Katapult, he manages the Arizona Flash Platform User Group (http://www.gotoandstop.org). All titles aside, John is a geek.
假设已经定义一个 User 类,使用如下的代码可以获取此类的公共属性和方法:
Type type_User = typeof(User);foreach (PropertyInfo p in type_User.GetProperties())//GetProperties 为获取所有公共属性
{ p.Name;// 属性的名称}foreach (MethodInfo m in type_User.GetMethods())//GetProperties 为获取所有公共属性 { m.Name;// 方法的名称}
如果要使用 PropertyInfo 和 MethodInfo 类需要引入 using System.Reflection; 命名空间。
详细信息查询 MSDN 帮助
SqlCommandBuilder.DeriveParameters (command),其中的 command 就是一个 SqlCommand 的对象,这个 command.CommandType = CommandType.StoredProcedure;
这样 command 的参数列表就被自动分配了
29 号的晚上 9 点多出发,到了这里将近凌晨!
夜晚,与温州并无多大区别,没有拍的冲动。
到宾馆,迅速连上网,发现没有网络日子不能过了,看看自己的 blog,发现速度还行,上海的机房网络确实不错。
宾馆,不高级,但挺干净的,满身疲惫,洗去满身 “风尘”,期待明天的行程!
QQ 的远程协助,开始用感觉还不错,如果有朋友有什么问题,可以直接发给我控制处理,但是最近更新了版本,却出现了这样的 Bug:
图形传输很卡,出现局部黑屏现象,可能是为了应付网络速度的原因,采用这样的方法,但是这样也造成了不便,附截图:
首先了解下 apollo 下这个类集 flash.desktop.* 的一些类
Function |
Description |
CeCopyFile | 复制文件 |
CeCreateDirectory | 创建目录 |
CeCreateFile | 创建,打开文件、管道、通讯资源、磁盘设备或者控制台。返回一个句柄用来访问对象。 |
CeDeleteFile | 删除文件 |
CeFindAllFiles | 从指定的 Windows CE 目录中获取所有文件和目录的信息,并且复制到一个包含 CE_FIND_DATA 结构的数组中 |
CeFindFirstFile | 在目录中查找匹配给定文件名的一个文件 |
CeFindClose | 关闭指定的查找句柄,CeFindFirstFile 和 CeFindNextFile 函数用这个句柄查找文件 |
CeFindNextFile | 从上一次访问的 CeFindFirstFile 继续查找文件 |
CeGetFileAttributes | 返回指定文件或目录的属性 |
CeGetFileSize | 获取指定文件的字节大小 |
CeGetFileTime | 获取文件创建日期时间,最后访问日期时间和最后修改日期时间 |
CeMoveFile | 移动(重命名)一个文件或者目录 |
CeReadFile | 从文件指针处读取文件数据 |
CeWriteFile | 从文件指针处写入文件数据 |