最近一直在使用 Flex3 對原有項目進行重構和 bug 修改。遇到不少性能問題,分析發現由于 Flex 在和 Servlet 交互時使用了大量的 XML 作為傳輸格式,導致某些功能在處理 XML 時非常的慢,甚至還 Error #1502: A script has executed for longer than 15 seconds。讓人痛不欲生!
和 RIA Meeting 組織者 lwz7512 交流后,他建議使用 BlazeDS 來代替 XML 操作。隨著對 BlazeDS 的漸漸了解,的確能解決不少性能問題。Adobe 的 Flex 技術傳道士 James Ward 在 BlazeBench: Why you want AMF and BlazeDS一文中詳細比較了利用 BlazeDS 所帶來的性能提升,下圖是 James Ward 寫的性能測試工具。
通過上面的測試數據發現,正如 BlazeDS 官方網站所提到的:在使用 AMF3 作為傳輸協議后,Flex 和后臺交互的性能大約提高了10倍。面對這一結果,想必大家很興奮,看來是時候用 BlazeDS 來替換 XML 了。不過問題并不是這么容易就解決了,由于 BlazeDS 在 2007年12月12日才正式開源發布,而在這之前項目都是以 XML 作為傳輸格式(當然也沒用GraniteDS),并且 Flex 代碼中處處可見對 XML 的操作,要對這樣的代碼進行重構......難。
要解決 XML 處理的性能問題。就應該好好的利用 E4X,盡量避免在解析 XML 的過程中使用循環。這里介紹幾篇文章讓大家了解下《E4X:出色的 JavaScript》、《E4X 教程》、《AS3中新的XML處理方法 – E4X》。E4X給我最大的便利就是..運算符。思考下面的XML:
????????????????
<
groups?
name
="大組"
>
????????????????????
<
group?
name
="小牛組"
>
????????????????????????
<
person?
fullname
="rosenjiang"
/>
????????????????????????
<
person?
fullname
="abc"
/>
????????????????????
</
group
>
????????????????????
<
group?
name
="柴雞組"
>
????????????????????????
<
person?
fullname
="rosenjiang"
/>
????????????????????
</
group
>
????????????????????
<
group?
name
="柴鴨組"
>
????????????????????????
<
person?
fullname
="rosenjiang"
/>
????????????????????????
<
person?
fullname
="rosen?jiang"
/>
????????????????????
</
group
>
????????????????????
<
group?
name
="獨立大隊"
>
????????????????????????
<
person?
fullname
="rosenjiang"
/>
????????????????????
</
group
>
???????????????????
</
groups
>
;
要得到所有屬性fullname是”rosenjiang”的person節點的個數怎么做?在沒詳細了解 E4X 之前,我會用 myXML.group 操作得到 group 的 XMLList 集合,然后再用循環去找尋每個 group 中 person 節點屬性 fullname 為”rosenjiang”的數據:
???????????????????var?list:XMLList? = ?myXML.group;
???????????????????var?count: int ? = ? 0 ;
??????????????????? for (var?i: int = 0 ;?i < list.length();?i ++ ){
???????????????????????var?persons:XMLList? = ?list[i].person;
??????????????????????? if (persons.length()? == ? 1 ? && ?persons.@fullname? == ? " rosenjiang " ){
???????????????????????????count? ++ ;
???????????????????????} else {
??????????????????????????? for (var?j: int = 0 ;?j < persons.length();?j ++ ){
??????????????????????????????? if (persons[j].@fullname? == ? " rosenjiang " ){
???????????????????????????????????count? ++ ;
???????????????????????????????}
???????????????????????????}
???????????????????????}
???????????????????}
???????????????????Alert.show(count + " 個 " );
???????????????}
上面的寫法的確很傻,下面是改進之后的代碼,關鍵部分只有一行:
???????????????????var?list:XMLList? = ?myXML..person.(@fullname? == ? " rosenjiang " );
???????????????????Alert.show(list.length() + " 個 " );
???????????????}
通過合理使用 E4X 語法,順利的避免了循環帶來的性能問題。過了幾天,來個新的需求,需要統計出在這個 XML 中有幾個不同姓名的 person。思考片刻,我可不可以用眼睛數出來啊?這里有 3 個...... 好吧,看來又是循環問題,第一個想到的是用兩個嵌套 for 循環來進行排除處理,這是最直觀的想法......
下面我介紹下如何用 ArrayCollection 并只使用一個循環來計算個數。由于 Flex 里面不支持 Map 類型,而我 Google 了一圈,且 RIACN 論壇上網友的 Map 實現性能都不行,遂打算用 ArrayCollection 模擬 Map 進行操作:
???????????????function?on_click(): void {
???????????????????var?list:XMLList? = ?myXML..person;
???????????????????var?ac:ArrayCollection? = ? new ?ArrayCollection();
???????????????? for ?each?(var?item:XML?in?list){
????????????????????var?fullname:String? = ?item.@fullname;
???????????????????? if (ac.getItemIndex(fullname)? == ? - 1 ){
????????????????????????ac.addItem(fullname);
????????????????????}
????????????????}
???????????????????Alert.show(ac.length + " 個 " );
???????????????}
上面代碼沒什么過多解釋,思路是取出一個 fullname 放進 ArrayCollection,然后判定下一個 fullname 是否存在于 ArrayCollection 中,如果存在就跳過,不存在就放進去再取下一個。另外我發現,使用 for each 比單純的使用 for 性能要高一點點。
做了以上的努力后,性能還是低下!怎么辦?看來沒什么辦法了,和你的 boss 談談吧,考慮下進行大刀闊斧重構的可能性?;蛘吣芊裨诔瑫r后給用戶一個提示,讓他操作的數據量少點,需要做的是捕獲超時異常,既 ScriptTimeoutError,請參閱http://www.cs.vu.nl/~eliens/pim/assets/flex3/langref/flash/errors/ScriptTimeoutError.html,進行 try catch 。 Good luck!