作ؓ当今L的Rich Internet Application应用q_QOpenLaszlo为用L面开发h员提供了强大的API来创建基于Flash的富客户端程序。虽然OpenLaszlo拥有z、快速的开发方式,但是我们仍然需要投入大量精力来x数据驱动的OpenLaszlo应用的性能。本文对如何开发高效的OpenLaszlo应用展开了深入的讨论。结合官方的开发指南,W者对如何获得满意的性能l出了自q最佛_c这些徏议主要涵盖了延缓初始化时间、惰性复制数据、缓存数据等三方面内容,q以具体的试验数据来说明它们的有效性?/p>
在Web应用来关注用户体验的今天Q传l的HTML应用已经不能满开发h员和l端用户的需求。OpenLaszlo作ؓ当今L的RIAQRich Internet ApplicationQ的应用q_Q在q入开源社Z后显C出了更大的zd。Laszlo的技术基于XML和JavaScript来构建RIAE序QؓWeb开发h员提供了一U简z快速的~程模式Qƈ且给l端用户以更加动态的交互式体验?/p>
OpenLaszlo的SDKQStandard Development KitQ由一个用Java~写的编译器、一个运行时的JavaScript库和一个可选的Java Servlet构成Q如?.1所C。开发OpenLaszlo的步骤非常简单:~辑、保存和h源文件即可。开发h员可以用Q何文本编辑器来编辑源文gQƈ且将其对应的URL键入览器。OpenLaszlo服务器自动地文件编译成一个Flash文gQ然后浏览器其展示出来?/p>
?.1 Laszlo服务器的M架构
OpenLaszlo应用由LZX的文件组成。LZX是一U标准驱动的XML和JavaScript描述性的语言Q它使得上述声明式的QdeclarativeQ、基于文本的开发流E变为可能。列?.2展示了一D늮单的OpenLaszloE序代码Q它定义了一个按钮,用户在按下之后该按钮的位|会向右Ud一D固定的距离?/p>
列表1.2 一个简单的OpenLaszlo应用
<canvas height="30"> <button onclick="animate('x', 100, 1000, true)"> Move me </button> </canvas> |
在下一章,我们根据OpenLaszlo官方的开发指南,构徏一个数据驱动的应用E序Qƈ对其q行深入的分析和调试?/p>
![]() ![]() |
![]()
|
Z更加清楚地说明我们的主题Q我们根据OpenLaszlo的官方Y件开发指南[1]Q扩展了其中通讯录的CZ。这个应用提供了一个功能齐全的联系人列表,它列Z所有的联系人,q允许用户对联系人添加、删除和更新。唯一不同的是Q我们的E序为用h供了更多的选项Q包含了更多的数据。图2.1展示了该E序的界面?/p>
?.1 一个通讯录的应用
׃联系人的功能和教E上的基本相同,我们使用了和OpenLaszlo指南中相同的Ҏ来实现它。列?.2描述了应用中一个类contactview的部分代码?/p>
列表2.2 联系图(contactviewQ的源代?/b>
<class name="contactview" extends="view" visible="false" x="20" height="180"> <simplelayout axis="y" spacing="5" /> <view layout="axis:x; spacing: 10"> <text y="10">First Name:</text> <edittext name="firstName" datapath="firstName/text()" y="10"/> <text y="10">Last Name:</text> <edittext name="lastname" datapath="lastName/text()" y="10"/> </view> <view> <text x="0" y="10">Gender:</text> <radiogroup layout="axis: x" x="80" y="12" datapath="gender" > <attribute name="genderv" type="string" value="$path{'text()'}" /> <method event="ondata"> this.selectItem(this.getAttribute('genderv')); </method> <radiobutton value="'M'" text="Male" /> <radiobutton value="'F'" text="Female"/> </radiogroup> <text x="195" y="10">Country:</text> <mycombobox x="275" y="10" width="105" datapath="country/text()"> <textlistitem value="${this.text}" datapath="timedata:/time/day/item/text()"/> </mycombobox> </view> </class> |
此外Q虽然应用的数据存储在本地机器的XML文g中,但如列表2.3所C,我们Ҏ据集的type和request属性做了设|,使应用程序在q行时才能够得到数据。在q种情况下,我们能够模拟OpenLaszlo的客L从远E的服务器中获得数据的情景?/p>
列表2.3 数据集的讄
<dataset name="countrydata" request="true" type="http" src="countries.xml"/> |
完成上述应用之后Q性能问题q速地昄出来。系l主要在以下三方面表现出了性能的瓶颈:
1. E序初始化的旉很长。当列表辑ֈ30个联pMh条目的规模时Q程序就会花费很长时间来昄整个列表。在E序装蝲的过E中Q这些条目逐条~慢地显C出来。而在此期_用户几乎不可能进行Q何操作?/p>
2. 下拉框(comboboxQ的初始化时间长度o人难以接受。当下拉框的选项非常多时Q由于控件会{待列表初始化完毕后才变为可见,q将会耗费大量的时间和内存?/p>
3. 删除功能的表C不尽如h意,其在用户尝试删除位于顶端的条目。当一个条目被删除Ӟ在其之下的条目将会被完全地刷新。这L重画界面也引发了严重的性能问题。鉴于以上的性能问题Q我们将逐步讨论如何解决q些问题。在W三章中Q我们会阐述一些能够提高性能的普遍原则,q应用到本章所演示的程序里。进一步的Q第四章描q本文所涉及的实验方法,q且向读者展现比较实验的l果?/p>
![]() ![]() |
![]()
|
在默认情况下QOpenLaszlo会在加蝲面时对所有的元素全部q行初始化。无些元素是可见或不可见的,它们都会被实例化。同ӞOpenLaszlo提供了initstage属性来控制何时执行节点的initҎ以及发送oninit事g的时机。initstage是LzNode的属性,换言之,它几乎可以被所有的元素l承q用,它有五种可选的属性值[2]Q?/p>
不难看出Q用late和defer的节点,在执行initҎ以及发送oninit事gӞq不一定被初始化完毕。因此,它们非常适用于g~OpenLaszlo节点的初始化旉。对一些在面初次加蝲时不可见的节点,我们可以使用initstage = defer来抑制初始化Q在触发其可见的事g上按需调用completeInstantiation。这h可以减少面初始化的旉Q又可以减少很多不必要的初始化。因为对用户而言Q很多不可见的元素是不需要被加蝲q系l的?/p>
具体到我们的通讯录应用中Q显CpMhl节的视图(contactviewQ完全符合上q的条g。于是,我们其initstage的属性设为deferQƈ且重写了其父节点的onclick事gQ得只有在用户点击了条目后Q细节视图才会占用系l的资源Q按需地被初始化。列?.1描述了具体的代码?/p>
列表3.1延缓初始化时间的代码
<view id="newEntryView"> <text text="New Entry..." > <method event="onclick"> parent.newContactView.setVisible(!parent.newContactView.visible); //在点L完成初始? parent.newContactView.completeInstantiation(); </method> </text> <contactview name="newContactView" datapath="newcontact:/contact" initstage="defer"> <!--在页面初ơ加载时不初始化contactview--> <button width="80" x="300" text="Add" /> </contactview> |
用户界面中,l常存在实际条目比展C给用户的条目多的列表。OpenLaszloU情况在baselist、basecombobox{节点中定义了dataoption属性。当dataoption取lazy值时Q列表的条目QlistitemQ将会用惰性复Ӟ卛_复制需要显C的条目。通讯录中表示国家的下拉框有近200个选项Q就属于q种情况。在列表3.2中,我们自定义了一个下拉框c,q上了它的使用CZ。这P即?00个数据项Q系l也只复制了5个textlistitem的视图?/p>
列表3.2 使用惰性复制的下拉?/b>
<class name="mycombobox" extends="combobox" editable="false" shownitems="5" dataoption="lazy" /> <mycombobox id="country" x="275" width="105" datapath="country/text()"> <textlistitem datapath="countrydata:/countries/country/text()" value="${this.text}" /> </mycombobox> |
值得一提的是,我们?lt;textlistitem>中直接定义了datapath属性,因此它承了父节点dataopiton = lazy的特征。如果在<textlistitem>下单独定义datapath元素Q那么我们还必须其replication属性设为lazyQ如?.3所C?/p>
列表3.3 单独声明datapath的textlistitem
<mycombobox > <textlistitem value="${this.text}" > <datapath xpath="countrydata:/countries/country/text()" replication = "lazy" /> </textlistitem> </mycombobox> |
事实上,OpenLaszlo使用惰性复制的列表使用LzLazyReplicationManager而不是缺省的LzReplicationManager来控制视图。后者在视图的datapath和多个datanode匚wӞ为每一个匹配都创徏一个视图;而前者出于显C数据的考虑Q只创徏_数量的视图。本文的例子中,通讯录的条目q多Ӟ如超q?00条)Q也可以考虑使用lazy的属性初始化时间变快?/p>
假OpenLaszlo应用使用了数据复Ӟ而且q些数据在运行时改变的话Q默认的LzReplicationManager会将数据改变所对应的视N毁,然后重新创徏它们。显而易见,如果数据集非常大Q这L调整{略会导致用L面严重的反应q缓。如W二章所qͼ我们的通讯录应用在删除位于列表端的记录时Q就会出现明昄延迟?/p>
Z解决上述问题Q我们可以将匚w多个数据节点的datapath中的pooling属性设为true。设|之后,LzLazyReplicationManager只是改变了的数据重新指向已l被创徏的视图。这LL面所反映出的数据更新速度会明昑֊快,具体的应用见列表3.4?/p>
列表3.4 使用pooling属?/b>
<view> <datapath xpath="dset:/phonebook/contact" pooling="true" /> <simplelayout axis="y" /> <view name="list" > <!-- more... --> </view> </view> |
![]() ![]() |
最后,我们对本文提及的实验进行性能的监和调试。根据上文的原则Q我们采用OpenLaszlo 3.3.3Q对性能优化前后的结果进行比较。表4.1展示了实验的具体配置?/p>
?.1 实验环境的配|?/b>
CPU
内存大小
Total paging space
览器类?/td>
1698MHz
512MB
1.22GB
Mozilla Firefox 1.5.0.4
量OpenLaszlo代码的性能分ؓ两种Q加载时间和响应旉。前者说明了OpenLaszlo的应用在多久之内被初始化Q而后者则是衡量程序能够响应用户动作的敏捷E度Q比如在通讯录应用中完成"删除"功能的时间长度。对于这两种旉Q我们都在程序开始和l尾记录旉Q然后将两者相减,得到最后的l果。列?.2展示了这U方法的实现代码?/p>
列表4.2 量响应旉
<button width="80" text="Delete"> <method event="onclick"> var pre = (new Date).getTime(); //记录开始执行时? parent.parent.parent.datapath.deleteNode(); var cur = (new Date).getTime(); //记录l束旉 debug.write(cur - pre); //输出 </method> </button> |
我们分别寚w讯录中?条?条和30条记录的情况做了比较。我们逐项应用上述的原则:所有表C时间的实验数据单位为毫U(msQ。最后的l果?0ơ操作的q_旉得到。首先,我们?"initstage = defer"前后的初始化旉的比较列在表4.3。可以看刎ͼ在联pMh的细节视图不被加载的时候,初始化的旉有了明显的加快?/p>
?.3使用defer的性能比较
记录数目 | initstage = deferQmsQ?/td> | initstage = normalQmsQ?/td> | 降低q度Q?Q?/td> |
3 | 268.4 | 3277.8 | 91.82 |
9 | 433.6 | 10816.8 | 95.99 |
30 | 955.2 | 54734.7 | 98.25 |
其次Q表4.4展示了在使用"pooling"前后的响应时间?/p>
?.4 使用pooling的响应时间比?/b>
记录数目
pooling = trueQmsQ?/td>
pooling = falseQmsQ?/td>
降低q度Q?Q?/td>
3
28.00
136.14
79.43
9
77.00
728.00
89.42
30
467.67
3079.25
84.81
最后,本文描述"lazy"选项的效果,如表4.5所C。不隄出,"lazy"选项与上q的选项不同Q没有随着记录的增多而对性能有显著的改善。显Ӟq是因ؓ"lazy"针对的是单个更新视图的初始化旉Q和M记录的条数没有明昄关系?/p>
?.5 使用lazy的性能比较
记录数目 | dataoption = trueQmsQ?/td> | dataoption = noneQmsQ?/td> | 降低q度Q?Q?/td> |
3 | 838 | 7213.6 | 88.38 |
9 | 852 | 7344.5 | 88.40 |
30 | 961.4 | 8587.1 | 88.80 |
![]() ![]() |
![]()
|