在Web應用越來越關注用戶體驗的今天,傳統的HTML應用已經不能滿足開發人員和終端用戶的需求。OpenLaszlo作為當今主流的RIA(Rich Internet Application)的應用平臺,在進入開源社區之后顯示出了更大的活力。Laszlo的技術基于XML和JavaScript來構建RIA程序,為Web開發人員提供了一種簡潔快速的編程模式,并且給終端用戶以更加動態的交互式體驗。
OpenLaszlo的SDK(Standard Development Kit)由一個用Java編寫的編譯器、一個運行時的JavaScript庫和一個可選的Java Servlet構成,如圖1.1所示。開發OpenLaszlo的步驟非常簡單:編輯、保存和刷新源文件即可。開發人員可以使用任何文本編輯器來編輯源文件,并且將其對應的URL鍵入瀏覽器。OpenLaszlo服務器自動地將文件編譯成一個Flash文件,然后瀏覽器將其展示出來。
?
?
OpenLaszlo應用由LZX的文件組成。LZX是一種標準驅動的XML和JavaScript描述性的語言,它使得上述聲明式的(declarative)、基于文本的開發流程變為可能。列表1.2展示了一段簡單的OpenLaszlo程序代碼,它定義了一個按鈕,用戶在按下之后該按鈕的位置會向右移動一段固定的距離。
<canvas height="30"> <button onclick="animate('x', 100, 1000, true)"> Move me </button> </canvas> |
?
在下一章,我們將根據OpenLaszlo官方的開發指南,構建一個數據驅動的應用程序,并對其進行深入的分析和調試。
?
|
? |
?
?
為了更加清楚地說明我們的主題,我們根據OpenLaszlo的官方軟件開發指南[1],擴展了其中通訊錄的示例。這個應用提供了一個功能齊全的聯系人列表,它列出了所有的聯系人,并允許用戶對聯系人添加、刪除和更新。唯一不同的是,我們的程序為用戶提供了更多的選項,包含了更多的數據。圖2.1展示了該程序的界面。
由于聯系人的功能和教程上的基本相同,我們使用了和OpenLaszlo指南中相同的方法來實現它。列表2.2描述了應用中一個類contactview的部分代碼。
<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> |
?
此外,雖然應用的數據存儲在本地機器的XML文件中,但如列表2.3所示,我們對數據集的type和request屬性做了設置,使應用程序在運行時才能夠得到數據。在這種情況下,我們能夠模擬OpenLaszlo的客戶端從遠程的服務器中獲得數據的情景。
<dataset name="countrydata" request="true" type="http" src="countries.xml"/> |
?
完成上述應用之后,性能問題迅速地顯現出來。系統主要在以下三方面表現出了性能的瓶頸:
1. 程序初始化的時間很長。當列表達到30個聯系人條目的規模時,程序就會花費很長時間來顯示整個列表。在程序裝載的過程中,這些條目逐條緩慢地顯示出來。而在此期間,用戶幾乎不可能進行任何操作。
2. 下拉框(combobox)的初始化時間長度令人難以接受。當下拉框的選項非常多時,由于控件會等待列表初始化完畢后才變為可見,這將會耗費大量的時間和內存。
3. 刪除功能的表現也不盡如人意,尤其在用戶嘗試刪除位于頂端的條目。當一個條目被刪除時,在其之下的條目將會被完全地刷新。這樣的重畫界面也引發了嚴重的性能問題。鑒于以上的性能問題,我們將逐步討論如何解決這些問題。在第三章中,我們會闡述一些能夠提高性能的普遍原則,并應用到本章所演示的程序里。進一步的,第四章將描述本文所涉及的實驗方法,并且向讀者展現比較實驗的結果。
?
|
|
?
在默認情況下,OpenLaszlo會在加載頁面時對所有的元素全部進行初始化。無論這些元素是可見或不可見的,它們都會被實例化。同時,OpenLaszlo提供了initstage屬性來控制何時執行節點的init方法以及發送oninit事件的時機。initstage是LzNode的屬性,換言之,它幾乎可以被所有的元素繼承并使用,它有五種可選的屬性值[2]:
-
immediate
除非該實例的所有子節點被創建,否則其他代碼都不得運行。也就是說,初始化是實例化的最后一個階段。 -
early
在視圖(view)和它的子節點被創建后,立即調用init方法。 -
normal
系統缺省值。Init方法在初始化父節點的工作完成后被調用。 -
late
在系統空閑時(idle)初始化節點。用戶可以通過檢查isinited屬性來確認節點是否被初始化完畢。如果想在某一時刻強制節點初始化,可以調用completeInstantiation方法。 -
defer
在該設置下,除非用戶顯式地調用completeInstantiation,否則節點將不會被初始化。
不難看出,使用late和defer的節點,在執行init方法以及發送oninit事件時,并不一定被初始化完畢。因此,它們非常適用于延緩OpenLaszlo節點的初始化時間。對一些在頁面初次加載時不可見的節點,我們可以使用initstage = defer來抑制初始化,在觸發其可見的事件上按需調用completeInstantiation。這樣既可以減少頁面初始化的時間,又可以減少很多不必要的初始化。因為對用戶而言,很多不可見的元素是不需要被加載進系統的。
具體到我們的通訊錄應用中,顯示聯系人細節的視圖(contactview)完全符合上述的條件。于是,我們將其initstage的屬性設為defer,并且重寫了其父節點的onclick事件,使得只有在用戶點擊了條目后,細節視圖才會占用系統的資源,按需地被初始化。列表3.1描述了具體的代碼。
<view id="newEntryView"> <text text="New Entry..." > <method event="onclick"> parent.newContactView.setVisible(!parent.newContactView.visible); //在點擊時完成初始化 parent.newContactView.completeInstantiation(); </method> </text> <contactview name="newContactView" datapath="newcontact:/contact" initstage="defer"> <!--在頁面初次加載時不初始化contactview--> <button width="80" x="300" text="Add" /> </contactview> |
?
用戶界面中,經常存在實際條目比展示給用戶的條目多的列表。OpenLaszlo為這種情況在baselist、basecombobox等節點中定義了dataoption屬性。當dataoption取lazy值時,列表的條目(listitem)將會使用惰性復制,即只復制需要顯示的條目。通訊錄中表示國家的下拉框有近200個選項,就屬于這種情況。在列表3.2中,我們自定義了一個下拉框類,并附上了它的使用示例。這樣,即使有200個數據項,系統也只復制了5個textlistitem的視圖。
<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> |
?
值得一提的是,我們在<textlistitem>中直接定義了datapath屬性,因此它繼承了父節點dataopiton = lazy的特征。如果在<textlistitem>下單獨定義datapath元素,那么我們還必須將其replication屬性設為lazy,如表3.3所示。
列表3.3 單獨聲明datapath的textlistitem
<mycombobox > <textlistitem value="${this.text}" > <datapath xpath="countrydata:/countries/country/text()" replication = "lazy" /> </textlistitem> </mycombobox> |
?
事實上,OpenLaszlo使用惰性復制的列表使用LzLazyReplicationManager而不是缺省的LzReplicationManager來控制視圖。后者在視圖的datapath和多個datanode匹配時,為每一個匹配都創建一個視圖;而前者出于顯示數據的考慮,只創建足夠數量的視圖。本文的例子中,通訊錄的條目過多時(如超過100條),也可以考慮使用lazy的屬性使初始化時間變快。
假使OpenLaszlo應用使用了數據復制,而且這些數據在運行時改變的話,默認的LzReplicationManager會將數據改變所對應的視圖銷毀,然后重新創建它們。顯而易見,如果數據集非常大,這樣的調整策略會導致用戶界面嚴重的反應遲緩。如第二章所述,我們的通訊錄應用在刪除位于列表頂端的記錄時,就會出現明顯的延遲。
為了解決上述問題,我們可以將匹配多個數據節點的datapath中的pooling屬性設為true。設置之后,LzLazyReplicationManager只是將改變了的數據重新指向已經被創建的視圖。這樣用戶界面所反映出的數據更新速度會明顯加快,具體的應用見列表3.4。
<view> <datapath xpath="dset:/phonebook/contact" pooling="true" /> <simplelayout axis="y" /> <view name="list" > <!-- more... --> </view> </view> |
?
|
|
?
最后,我們將對本文提及的實驗進行性能的監測和調試。根據上文的原則,我們采用OpenLaszlo 3.3.3,對性能優化前后的結果進行比較。表4.1展示了實驗的具體配置。
表4.1 實驗環境的配置
CPU | 內存大小 | Total paging space | 瀏覽器類型 |
1698MHz | 512MB | 1.22GB | Mozilla Firefox 1.5.0.4 |
測量OpenLaszlo代碼的性能分為兩種:加載時間和響應時間。前者說明了OpenLaszlo的應用在多久之內被初始化,而后者則是衡量程序能夠響應用戶動作的敏捷程度,比如在通訊錄應用中完成"刪除"功能的時間長度。對于這兩種時間,我們都在程序開始和結尾記錄時間,然后將兩者相減,得到最后的結果。列表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(); //記錄結束時間 debug.write(cur - pre); //輸出 </method> </button> |
?
我們分別對通訊錄中有3條、9條和30條記錄的情況做了比較。我們逐項應用上述的原則:所有表示時間的實驗數據單位為毫秒(ms)。最后的結果由10次操作的平均時間得到。首先,我們將 "initstage = defer"前后的初始化時間的比較列在表4.3。可以看到,在聯系人的細節視圖不被加載的時候,初始化的時間有了明顯的加快。
表4.3使用defer的性能比較
記錄數目 | initstage = defer(ms) | initstage = normal(ms) | 降低幅度(%) |
3 | 268.4 | 3277.8 | 91.82 |
9 | 433.6 | 10816.8 | 95.99 |
30 | 955.2 | 54734.7 | 98.25 |
其次,表4.4展示了在使用"pooling"前后的響應時間。
表4.4 使用pooling的響應時間比較
記錄數目 | pooling = true(ms) | pooling = false(ms) | 降低幅度(%) |
3 | 28.00 | 136.14 | 79.43 |
9 | 77.00 | 728.00 | 89.42 |
30 | 467.67 | 3079.25 | 84.81 |
最后,本文描述"lazy"選項的效果,如表4.5所示。不難看出,"lazy"選項與上述的選項不同,沒有隨著記錄的增多而對性能有顯著的改善。顯然,這是因為"lazy"針對的是單個更新視圖的初始化時間,和總體記錄的條數沒有明顯的關系。
表4.5 使用lazy的性能比較
記錄數目 | dataoption = true(ms) | dataoption = none(ms) | 降低幅度(%) |
3 | 838 | 7213.6 | 88.38 |
9 | 852 | 7344.5 | 88.40 |
30 | 961.4 | 8587.1 | 88.80 |
?
|
|
?
本文為OpenLaszlo的開發者提供了一些提高程序性能的技巧,具體包括:延緩初始化的時間、惰性復制和緩存數據。我們通過擴展OpenLaszlo開發指南中的示例,向讀者展示了一個略微復雜的通訊錄應用。以其為場景分析了系統的性能瓶頸,將作者所總結出的技巧應用于其上。最后,我們還對每個具體的原則做了實驗,比較了性能的差異。從我們的實驗中可以看到,本文所提及的原則對性能的提高有著顯著的作用。相應的用戶體驗也有了明顯的提升,達到了用RIA開發的目的。
在今后的工作中,我們將進一步優化這個示例,使其成為一個能和后臺Java Server進行交互的應用,并開發出相應的OpenLaszlo組件。更進一步的,我們會在此基礎上為應用添加更多(諸如全球化)的特性。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
