posts - 431,  comments - 344,  trackbacks - 0
          本文對(duì)如何開(kāi)發(fā)高效的OpenLaszlo應(yīng)用展開(kāi)了深入的討論。結(jié)合官方的開(kāi)發(fā)指南,筆者對(duì)如何獲得滿意的性能給出了自己的最佳實(shí)踐。這些建議主要涵蓋了延緩初始化時(shí)間、惰性復(fù)制數(shù)據(jù)、緩存數(shù)據(jù)等三方面內(nèi)容。

          作為當(dāng)今主流的Rich Internet Application應(yīng)用平臺(tái),OpenLaszlo為用戶界面開(kāi)發(fā)人員提供了強(qiáng)大的API來(lái)創(chuàng)建基于Flash的富客戶端程序。雖然OpenLaszlo擁有簡(jiǎn)潔、快速的開(kāi)發(fā)方式,但是我們?nèi)匀恍枰度氪罅烤?lái)關(guān)注數(shù)據(jù)驅(qū)動(dòng)的OpenLaszlo應(yīng)用的性能。本文對(duì)如何開(kāi)發(fā)高效的OpenLaszlo應(yīng)用展開(kāi)了深入的討論。結(jié)合官方的開(kāi)發(fā)指南,筆者對(duì)如何獲得滿意的性能給出了自己的最佳實(shí)踐。這些建議主要涵蓋了延緩初始化時(shí)間、惰性復(fù)制數(shù)據(jù)、緩存數(shù)據(jù)等三方面內(nèi)容,并以具體的試驗(yàn)數(shù)據(jù)來(lái)說(shuō)明它們的有效性。

          1. OpenLaszlo簡(jiǎn)介

          在Web應(yīng)用越來(lái)越關(guān)注用戶體驗(yàn)的今天,傳統(tǒng)的HTML應(yīng)用已經(jīng)不能滿足開(kāi)發(fā)人員和終端用戶的需求。OpenLaszlo作為當(dāng)今主流的RIA(Rich Internet Application)的應(yīng)用平臺(tái),在進(jìn)入開(kāi)源社區(qū)之后顯示出了更大的活力。Laszlo的技術(shù)基于XML和JavaScript來(lái)構(gòu)建RIA程序,為Web開(kāi)發(fā)人員提供了一種簡(jiǎn)潔快速的編程模式,并且給終端用戶以更加動(dòng)態(tài)的交互式體驗(yàn)。

          OpenLaszlo的SDK(Standard Development Kit)由一個(gè)用Java編寫(xiě)的編譯器、一個(gè)運(yùn)行時(shí)的JavaScript庫(kù)和一個(gè)可選的Java Servlet構(gòu)成,如圖1.1所示。開(kāi)發(fā)OpenLaszlo的步驟非常簡(jiǎn)單:編輯、保存和刷新源文件即可。開(kāi)發(fā)人員可以使用任何文本編輯器來(lái)編輯源文件,并且將其對(duì)應(yīng)的URL鍵入瀏覽器。OpenLaszlo服務(wù)器自動(dòng)地將文件編譯成一個(gè)Flash文件,然后瀏覽器將其展示出來(lái)。


          圖1.1 Laszlo服務(wù)器的總體架構(gòu)
          圖1.1 Laszlo服務(wù)器的總體架構(gòu)

          OpenLaszlo應(yīng)用由LZX的文件組成。LZX是一種標(biāo)準(zhǔn)驅(qū)動(dòng)的XML和JavaScript描述性的語(yǔ)言,它使得上述聲明式的(declarative)、基于文本的開(kāi)發(fā)流程變?yōu)榭赡堋A斜?.2展示了一段簡(jiǎn)單的OpenLaszlo程序代碼,它定義了一個(gè)按鈕,用戶在按下之后該按鈕的位置會(huì)向右移動(dòng)一段固定的距離。


          列表1.2 一個(gè)簡(jiǎn)單的OpenLaszlo應(yīng)用
          <canvas height="30">
            <button onclick="animate('x', 100, 1000, true)">
              Move me
            </button>
          </canvas>
          

          在下一章,我們將根據(jù)OpenLaszlo官方的開(kāi)發(fā)指南,構(gòu)建一個(gè)數(shù)據(jù)驅(qū)動(dòng)的應(yīng)用程序,并對(duì)其進(jìn)行深入的分析和調(diào)試。







          2. 開(kāi)發(fā)數(shù)據(jù)驅(qū)動(dòng)的應(yīng)用程序

          2.1 一個(gè)典型的應(yīng)用場(chǎng)景

          為了更加清楚地說(shuō)明我們的主題,我們根據(jù)OpenLaszlo的官方軟件開(kāi)發(fā)指南[1],擴(kuò)展了其中通訊錄的示例。這個(gè)應(yīng)用提供了一個(gè)功能齊全的聯(lián)系人列表,它列出了所有的聯(lián)系人,并允許用戶對(duì)聯(lián)系人添加、刪除和更新。唯一不同的是,我們的程序?yàn)橛脩籼峁┝烁嗟倪x項(xiàng),包含了更多的數(shù)據(jù)。圖2.1展示了該程序的界面。


          圖2.1 一個(gè)通訊錄的應(yīng)用
          圖2.1 一個(gè)通訊錄的應(yīng)用

          由于聯(lián)系人的功能和教程上的基本相同,我們使用了和OpenLaszlo指南中相同的方法來(lái)實(shí)現(xiàn)它。列表2.2描述了應(yīng)用中一個(gè)類contactview的部分代碼。


          列表2.2 聯(lián)系人視圖(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>
          

          此外,雖然應(yīng)用的數(shù)據(jù)存儲(chǔ)在本地機(jī)器的XML文件中,但如列表2.3所示,我們對(duì)數(shù)據(jù)集的type和request屬性做了設(shè)置,使應(yīng)用程序在運(yùn)行時(shí)才能夠得到數(shù)據(jù)。在這種情況下,我們能夠模擬OpenLaszlo的客戶端從遠(yuǎn)程的服務(wù)器中獲得數(shù)據(jù)的情景。


          列表2.3 數(shù)據(jù)集的設(shè)置
          <dataset name="countrydata" request="true" type="http" src="countries.xml"/>
          

          2.2 系統(tǒng)瓶頸

          完成上述應(yīng)用之后,性能問(wèn)題迅速地顯現(xiàn)出來(lái)。系統(tǒng)主要在以下三方面表現(xiàn)出了性能的瓶頸:

          1. 程序初始化的時(shí)間很長(zhǎng)。當(dāng)列表達(dá)到30個(gè)聯(lián)系人條目的規(guī)模時(shí),程序就會(huì)花費(fèi)很長(zhǎng)時(shí)間來(lái)顯示整個(gè)列表。在程序裝載的過(guò)程中,這些條目逐條緩慢地顯示出來(lái)。而在此期間,用戶幾乎不可能進(jìn)行任何操作。

          2. 下拉框(combobox)的初始化時(shí)間長(zhǎng)度令人難以接受。當(dāng)下拉框的選項(xiàng)非常多時(shí),由于控件會(huì)等待列表初始化完畢后才變?yōu)榭梢?jiàn),這將會(huì)耗費(fèi)大量的時(shí)間和內(nèi)存。

          3. 刪除功能的表現(xiàn)也不盡如人意,尤其在用戶嘗試刪除位于頂端的條目。當(dāng)一個(gè)條目被刪除時(shí),在其之下的條目將會(huì)被完全地刷新。這樣的重畫(huà)界面也引發(fā)了嚴(yán)重的性能問(wèn)題。鑒于以上的性能問(wèn)題,我們將逐步討論如何解決這些問(wèn)題。在第三章中,我們會(huì)闡述一些能夠提高性能的普遍原則,并應(yīng)用到本章所演示的程序里。進(jìn)一步的,第四章將描述本文所涉及的實(shí)驗(yàn)方法,并且向讀者展現(xiàn)比較實(shí)驗(yàn)的結(jié)果。






          3. 優(yōu)化OpenLaszlo程序的原則

          3.1 延緩初始化時(shí)間

          在默認(rèn)情況下,OpenLaszlo會(huì)在加載頁(yè)面時(shí)對(duì)所有的元素全部進(jìn)行初始化。無(wú)論這些元素是可見(jiàn)或不可見(jiàn)的,它們都會(huì)被實(shí)例化。同時(shí),OpenLaszlo提供了initstage屬性來(lái)控制何時(shí)執(zhí)行節(jié)點(diǎn)的init方法以及發(fā)送oninit事件的時(shí)機(jī)。initstage是LzNode的屬性,換言之,它幾乎可以被所有的元素繼承并使用,它有五種可選的屬性值[2]:

          • immediate
            除非該實(shí)例的所有子節(jié)點(diǎn)被創(chuàng)建,否則其他代碼都不得運(yùn)行。也就是說(shuō),初始化是實(shí)例化的最后一個(gè)階段。
          • early
            在視圖(view)和它的子節(jié)點(diǎn)被創(chuàng)建后,立即調(diào)用init方法。
          • normal
            系統(tǒng)缺省值。Init方法在初始化父節(jié)點(diǎn)的工作完成后被調(diào)用。
          • late
            在系統(tǒng)空閑時(shí)(idle)初始化節(jié)點(diǎn)。用戶可以通過(guò)檢查isinited屬性來(lái)確認(rèn)節(jié)點(diǎn)是否被初始化完畢。如果想在某一時(shí)刻強(qiáng)制節(jié)點(diǎn)初始化,可以調(diào)用completeInstantiation方法。
          • defer
            在該設(shè)置下,除非用戶顯式地調(diào)用completeInstantiation,否則節(jié)點(diǎn)將不會(huì)被初始化。

          不難看出,使用late和defer的節(jié)點(diǎn),在執(zhí)行init方法以及發(fā)送oninit事件時(shí),并不一定被初始化完畢。因此,它們非常適用于延緩OpenLaszlo節(jié)點(diǎn)的初始化時(shí)間。對(duì)一些在頁(yè)面初次加載時(shí)不可見(jiàn)的節(jié)點(diǎn),我們可以使用initstage = defer來(lái)抑制初始化,在觸發(fā)其可見(jiàn)的事件上按需調(diào)用completeInstantiation。這樣既可以減少頁(yè)面初始化的時(shí)間,又可以減少很多不必要的初始化。因?yàn)閷?duì)用戶而言,很多不可見(jiàn)的元素是不需要被加載進(jìn)系統(tǒng)的。

          具體到我們的通訊錄應(yīng)用中,顯示聯(lián)系人細(xì)節(jié)的視圖(contactview)完全符合上述的條件。于是,我們將其initstage的屬性設(shè)為defer,并且重寫(xiě)了其父節(jié)點(diǎn)的onclick事件,使得只有在用戶點(diǎn)擊了條目后,細(xì)節(jié)視圖才會(huì)占用系統(tǒng)的資源,按需地被初始化。列表3.1描述了具體的代碼。


          列表3.1延緩初始化時(shí)間的代碼
          <view id="newEntryView">
              <text text="New Entry..." > 
                  <method event="onclick">
                          parent.newContactView.setVisible(!parent.newContactView.visible);
                          //在點(diǎn)擊時(shí)完成初始化
                          parent.newContactView.completeInstantiation(); 
                  </method>
          </text>
          <contactview name="newContactView" 
               datapath="newcontact:/contact"
               initstage="defer"> <!--在頁(yè)面初次加載時(shí)不初始化contactview-->
               <button width="80" x="300" text="Add" />
          </contactview>
          

          3.2 惰性復(fù)制數(shù)據(jù)

          用戶界面中,經(jīng)常存在實(shí)際條目比展示給用戶的條目多的列表。OpenLaszlo為這種情況在baselist、basecombobox等節(jié)點(diǎn)中定義了dataoption屬性。當(dāng)dataoption取lazy值時(shí),列表的條目(listitem)將會(huì)使用惰性復(fù)制,即只復(fù)制需要顯示的條目。通訊錄中表示國(guó)家的下拉框有近200個(gè)選項(xiàng),就屬于這種情況。在列表3.2中,我們自定義了一個(gè)下拉框類,并附上了它的使用示例。這樣,即使有200個(gè)數(shù)據(jù)項(xiàng),系統(tǒng)也只復(fù)制了5個(gè)textlistitem的視圖。


          列表3.2 使用惰性復(fù)制的下拉框
          <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>
          

          值得一提的是,我們?cè)?lt;textlistitem>中直接定義了datapath屬性,因此它繼承了父節(jié)點(diǎn)dataopiton = lazy的特征。如果在<textlistitem>下單獨(dú)定義datapath元素,那么我們還必須將其replication屬性設(shè)為lazy,如表3.3所示。


          列表3.3 單獨(dú)聲明datapath的textlistitem
          <mycombobox >
          <textlistitem value="${this.text}" >
              <datapath xpath="countrydata:/countries/country/text()" 
                   replication = "lazy" />
          </textlistitem>
          </mycombobox>
          

          事實(shí)上,OpenLaszlo使用惰性復(fù)制的列表使用LzLazyReplicationManager而不是缺省的LzReplicationManager來(lái)控制視圖。后者在視圖的datapath和多個(gè)datanode匹配時(shí),為每一個(gè)匹配都創(chuàng)建一個(gè)視圖;而前者出于顯示數(shù)據(jù)的考慮,只創(chuàng)建足夠數(shù)量的視圖。本文的例子中,通訊錄的條目過(guò)多時(shí)(如超過(guò)100條),也可以考慮使用lazy的屬性使初始化時(shí)間變快。

          3.3 緩存數(shù)據(jù)

          假使OpenLaszlo應(yīng)用使用了數(shù)據(jù)復(fù)制,而且這些數(shù)據(jù)在運(yùn)行時(shí)改變的話,默認(rèn)的LzReplicationManager會(huì)將數(shù)據(jù)改變所對(duì)應(yīng)的視圖銷毀,然后重新創(chuàng)建它們。顯而易見(jiàn),如果數(shù)據(jù)集非常大,這樣的調(diào)整策略會(huì)導(dǎo)致用戶界面嚴(yán)重的反應(yīng)遲緩。如第二章所述,我們的通訊錄應(yīng)用在刪除位于列表頂端的記錄時(shí),就會(huì)出現(xiàn)明顯的延遲。

          為了解決上述問(wèn)題,我們可以將匹配多個(gè)數(shù)據(jù)節(jié)點(diǎn)的datapath中的pooling屬性設(shè)為true。設(shè)置之后,LzLazyReplicationManager只是將改變了的數(shù)據(jù)重新指向已經(jīng)被創(chuàng)建的視圖。這樣用戶界面所反映出的數(shù)據(jù)更新速度會(huì)明顯加快,具體的應(yīng)用見(jiàn)列表3.4。


          列表3.4 使用pooling屬性
          <view>
              <datapath xpath="dset:/phonebook/contact" pooling="true" />
              <simplelayout axis="y" />
              <view name="list" >
                <!-- more... -->
              </view>
          </view>
          





          4. 性能比較和分析

          4.1 測(cè)試方法和模型

          最后,我們將對(duì)本文提及的實(shí)驗(yàn)進(jìn)行性能的監(jiān)測(cè)和調(diào)試。根據(jù)上文的原則,我們采用OpenLaszlo 3.3.3,對(duì)性能優(yōu)化前后的結(jié)果進(jìn)行比較。表4.1展示了實(shí)驗(yàn)的具體配置。


          表4.1 實(shí)驗(yàn)環(huán)境的配置
          CPU 內(nèi)存大小 Total paging space 瀏覽器類型
          1698MHz 512MB 1.22GB Mozilla Firefox 1.5.0.4

          測(cè)量OpenLaszlo代碼的性能分為兩種:加載時(shí)間和響應(yīng)時(shí)間。前者說(shuō)明了OpenLaszlo的應(yīng)用在多久之內(nèi)被初始化,而后者則是衡量程序能夠響應(yīng)用戶動(dòng)作的敏捷程度,比如在通訊錄應(yīng)用中完成"刪除"功能的時(shí)間長(zhǎng)度。對(duì)于這兩種時(shí)間,我們都在程序開(kāi)始和結(jié)尾記錄時(shí)間,然后將兩者相減,得到最后的結(jié)果。列表4.2展示了這種方法的實(shí)現(xiàn)代碼。


          列表4.2 測(cè)量響應(yīng)時(shí)間
          <button width="80" text="Delete">
              <method event="onclick">
                  var pre = (new Date).getTime(); //記錄開(kāi)始執(zhí)行時(shí)間
                  parent.parent.parent.datapath.deleteNode();
                  var cur = (new Date).getTime(); //記錄結(jié)束時(shí)間
                  debug.write(cur - pre); //輸出
              </method>
          </button>
          

          4.2 測(cè)試結(jié)果

          我們分別對(duì)通訊錄中有3條、9條和30條記錄的情況做了比較。我們逐項(xiàng)應(yīng)用上述的原則:所有表示時(shí)間的實(shí)驗(yàn)數(shù)據(jù)單位為毫秒(ms)。最后的結(jié)果由10次操作的平均時(shí)間得到。首先,我們將 "initstage = defer"前后的初始化時(shí)間的比較列在表4.3。可以看到,在聯(lián)系人的細(xì)節(jié)視圖不被加載的時(shí)候,初始化的時(shí)間有了明顯的加快。


          表4.3使用defer的性能比較
          記錄數(shù)目 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"前后的響應(yīng)時(shí)間。


          表4.4 使用pooling的響應(yīng)時(shí)間比較
          記錄數(shù)目 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"選項(xiàng)的效果,如表4.5所示。不難看出,"lazy"選項(xiàng)與上述的選項(xiàng)不同,沒(méi)有隨著記錄的增多而對(duì)性能有顯著的改善。顯然,這是因?yàn)?lazy"針對(duì)的是單個(gè)更新視圖的初始化時(shí)間,和總體記錄的條數(shù)沒(méi)有明顯的關(guān)系。


          表4.5 使用lazy的性能比較
          記錄數(shù)目 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






          參考資料

          posted on 2007-01-30 23:02 周銳 閱讀(438) 評(píng)論(0)  編輯  收藏 所屬分類: RIA

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 石家庄市| 化隆| 都昌县| 乌苏市| 许昌县| 华池县| 曲沃县| 太湖县| 历史| 巩留县| 福海县| 汉沽区| 玛纳斯县| 合作市| 屏东县| 韶山市| 安国市| 广汉市| 兴仁县| 阜南县| 汾西县| 嘉禾县| 长丰县| 滦南县| 措美县| 富川| 石景山区| 澎湖县| 微山县| 翼城县| 泸溪县| 巴里| 凤山县| 汉沽区| 白河县| 宜城市| 合山市| 吴旗县| 缙云县| 明水县| 惠来县|