ActionScript 3 和 Flex框架的性能優化

轉載自 xukunwzq
最終編輯 xukunwzq

與其在程序寫完了之後臃腫得跑不動,不如平時注意這些關鍵點,時時提醒自己。翻譯出來,以便以後時時查閱。

1 創建新數組時避免使用它的構造函數。

   這樣做:var a = [];

   而不要這樣做:var a = new Array();

2 創建數組是一個消耗量很大的操作,所以請謹慎進行以下類型的操作:

   var vanityCollection01 : Array = new Array();

   var vanityCollection02 : Array = new Array();

   var vanityCollection03 : Array = new Array();

   var vanityCollection04 : Array = new Array();

3 複製一個數組最快的方式是:

   var copy : Array = sourceArray.concat();

4 無論你用哪種方式,爲數組的元素設置值都是一個慢的操作。

   employees.push( employee );

   employees[2] = employee;

5 在數組中獲得一個值的速度是設置一個值的二倍。

   var employee : Employee = employees[2];

6 將屬性函數設置爲靜態函數,這樣你在使用它的時候就不用實例化一個該類的對象。

   StringUtils.trim( “text with space at end ” );

   類定義:

   package

   {

   public final class StringUtils

   {

   public static function trim( s : String ) : String

   {

   var trimmed : String;

   // 邏輯實現代碼

   return trimmed;

   }

   }

   }

7 使用常量關鍵字const來定義那些在程序運行週期內都不會發生值改變的屬性。

   public const APPLICATION_PUBLISHER : String = “Company, Inc.”;

8 當一個類不再需要有子類的時候,將它定義爲final類。

   public final class StringUtils

9 巨長的函數名和變量名在Action Script 3中不會造成任何額外的消耗,(在其他語言中也是)

   someCrazyLongMethodNameDoesntReallyImpactPerformanceTooMuch();

10 在單行內定義多個變量不會帶來任何性能的提升(在其他語言中也是)

   var i=0; j=10; k=200;

11 使用if和使用switch做邏輯判斷所消耗的內存是沒有區別的,例如:

   if ( condition )

   {

   // 處理條件下的邏輯

   }

   跟使用switch

   switch ( condition )

   {

   case “A”:

   // A條件下的處理邏輯

   break;

   case “B”:

   // B條件下的處理邏輯

   break;

   }

   沒有任何內存消耗上的區別。

12 使用if做邏輯判斷時,儘可能的按照最有可能發生的情況的順序來順序排列。例如:

if ( 最有可能發生的情況 )

{

   // 處理最有可能發生的情況。

}

else if ( 有時候會發生的情況 )

{

   // 處理有時候會發生的情況。

}

else

{

   // 處理以上判斷都沒有發生時的情況 。

}

13 AVM在循環體內部進行計算時,將整型int數據提升爲浮點型Number進行處理,(從fp9到fp10,虛擬機已經有所改變,int,uint,number之間的轉換不再像之前那麼慢了。)

14 注意解決類型轉換,未知類型(unknown),非法類型(incorrect)的問題。

15 慎重使用 uint ,它會使程序變慢。

   var footerHex : uint = 0×00ccff;

16 在迭代器中使用整型作爲增長量

   應該這樣使用:

   for(var i: int = 0; i < n; i++)

   而不是:

   for (var i: Number = 0; i < n; i++)

17 不要爲int型變量賦小數值。

   應該這樣用:

   var decimal : Number = 14.654;

   不應該:

   var decimal : int = 14.654;

18 乘法 vs 除法:使用 5000*0.001 來替代 5000/1000。

19 如果你要在for或者while循環體內頻繁的使用一個值,請使用一個本地變量來存放它,而不是去頻繁的計算它。

   與其這樣頻繁的計算它:

   for (..){ a * 180 / Math.PI; }

   不如定義一個變量來存放它:

   var toRadians:Number = a*180/Math.PI;

20 避免在循環體判斷條件中進行計算,例如:

   var len : int = myArray.lengh;

   for (var i=0;i<len;i++){}

   而不要這樣做:

   for (var i=0;i< myArray.lengh;i++){ } (靠!我一直都這麼幹的!)

21 使用正則表達式來進行字符串檢查,並使用字符串函數來進行字符串搜索。

   例如:使用正則表達式做郵政編碼檢驗

   private var regEx:RegExp = /^[A-Z][0-9][A-Z] [0-9][A-Z][0-9]$/i;

   private function validatePostal( event : Event ) : void

   {

   if( regEx.test( zipTextInput.text ) )

   {

   // 處理輸入格式滿足的情況

   }

   }

   使用字符串函數處理字符串查詢:

   var string : String = “Search me”;

   var searchIndex : int = string.indexOf( “me” );

   var search : String = string.substring( searchIndex, searchIndex + 2 );

22 儘量重複使用那些屬於“內存高消耗區”的對象,例如,DisplayObjects,URLLoader。

23 借鑑Flex對象的設計模式:

   createChildren();

   commitProperties();

   updateDisplayList();

24 把使用Datagrids組件作爲你最後的顯示手段(如果你確信你真的沒有辦法使用一個常規的list實現你想要的功能,才使用它)

25 避免使用迭代器迭代具備滾動功能的數據。

26 避免使用setStyle()函數(這在Flex框架裏是性能消耗量最大的行爲之一)

27 使用過多的容器嵌套勢必會降低你程序的性能。例如下面這個噁心的嵌套。

<mx:Panel>

   <mx:VBox>

   <mx:HBox>

   <mx:Label text=”Label 1″ />

   <mx:VBox>

   <mx:Label text=”Label 2″ />

   </mx:VBox>

   <mx:HBox>

   <mx:Label text=”Label 3″ />

   <mx:VBox>

   <mx:Label text=”Label 4″ />

   </mx:VBox>

   </mx:HBox>

   </mx:HBox>

   </mx:VBox>

</mx:Panel>

28 你不用爲每個容器都加上命名空間的標籤,只有頂級容器需要這樣做。下面這個就是不必要的。

   <mx:Image xmlns:mx=”http://www.adobe.com/2006/mxml”

   source=”avatar.jpg” width=”200″ height=”200″ />

29 移除不必要的容器來減少容器嵌套。

30 避免在標籤內嵌套VBox容器(消除冗餘)

<mx:Panel>

   <mx:Label text=”Label 1″ />

   <mx:Label text=”Label 2″ />

</mx:Panel>

<mx:Panel>

   <mx:VBox>

   <mx:Label text=”Label 1″ />

   <mx:Label text=”Label 2″ />

   </mx:VBox>

</mx:Panel>

31 在mx:Application 標籤內部儘量避免使用VBox標籤。(消除冗餘)

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml>

   <mx:Label text=”Label 1″ />

   <mx:Label text=”Label 2″ />

</mx:Application>

而不要:

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml>

   <mx:VBox>

   <mx:Label text=”Label 1″ />

   <mx:Label text=”Label 2″ />

   </mx:VBox>

</mx:Application>

32 設置Repeater的recycleChildren屬性爲true可以提升它的性能(使用之前創建過的對象,而不是創建一個新對象)

<mx:Script>

   <![CDATA[

   [Bindable]

   public var repeaterData : Array = ["data 1", "data 2"];

   ]]>

</mx:Script>

<mx:Repeater id=”repeater” dataProvider=”{repeaterData}” recycleChildren=”true”>

   <mx:Label text=”data item: {repeater.currentItem}”/>

</mx:Repeater>

33 將幀頻(framerate)設置爲60或者更低。

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=http://www.adobe.com/2006/mxml

   frameRate=”45″>

</mx:Application>

34 避免在每一幀內處理多個顯示對象。

35 使用ENTER_FRAME 事件取代Timer事件

使用:

public function onEnterFrame( event : Event ) : void

{

}

private function init() : void

{

   addEventListener( Event.ENTER_FRAME, onEnterFrame );

}

而不要使用:

public function onTimerTick( event : Event ) : void

{

}

private function init() : void

{

   var timer : Timer = new Timer();

   timer.start();

   timer.addEventListener( TimerEvent.TIMER, onTimerTick );

}

36 在多幀中使用顯示對象時,使用以下方法推遲它的對象創建:

<mx:Container creationPolicy=”queued”/>

37 alpha = 0 並不等同於 visible = false(對象在不可見時將會不會被處理)

所以,使用:

loginButton.visible = false;

而不是:

loginButton.alpha = 0;

 

隨着Flex越來越多的被人們所熟知,越來越多的互聯網也開始了RIA應用。衆所周知,目前國內的寬帶應用並不是像很多發達國家發達,個人應用帶寬基本上都是2M以下的,怎麼樣能夠使你的Flex應用能夠流暢的運行在客戶端的問題,成爲了制約每個Flex應用開發程序員的大難題。
在這裏,我收集整理了一下網絡上關於這方面經驗,歡迎大家補充。

基本原則:
1. 從外部加載媒體(Media)
   Heider提到了一個常用的Flex最佳實踐——限制嵌入到應用/SWF文件中的媒體的數量,如圖像、影片及mp3等資源都可以從外部的SWF文件加載。 
Flex框架可以直接將圖片、mp3及字體等資源編譯到SWF中。當你想讓最終用戶獲得全部資源時,這種方式確實能派上用場,但是這會導致你的應用長時間停留在“Loading”階段。 

2. 在嵌入式字體中限制字符集
Heider建議在嵌入式字體中限制字符集以降低SWF文件的總下載時間: 
當你在Flex中嵌入一種字體時,你就會獲得該字體的全部字符的支持。儘管這可能是你想要的,但你確信你需要全部字符麼?例如,在一個只面向英文的應用中,你確信你真的想花時間下載中文字符數據麼?
3. 緩存框架 
       Heider回顧了Flex 3 support for runtime-shared-libraries (RSL)這篇文章:

從Flex 3開始,你可以將Adobe簽名的框架——RSLs緩存到Flash Player的cache中。這有兩個好處。首先,緩存在Flash Player cache中的簽名的框架RSLs可由所有配置好的Flex應用共享。換句話說,如果某人的應用已經下載了500k的簽名的框架RSL,並且該RSL仍舊 在Flash Player cache中,那麼你的應用就可以使用緩存下來的RSL。其次,即使某人清空了其瀏覽器緩存,對Flash Player cache也沒有任何影響。
4. 考慮模塊化 
Heider談到了將Flex應用劃分成模塊的好處:減少字體加載時間的另一種方式就是將你的Flex應用劃分成模塊。使用模塊的一個好處在於當加載和卸載模塊時你能完全操控它。
之所以要劃分成模塊的最後一個原因是他們更快,而且我能即時加載它們。換句話說,在啓動時唯一需要加載的模塊就是 Step1.swf模塊。因此,在使用模塊的情況下,最終用戶節省了啓動時間,但是當他從一個模塊切換到另一個模塊時卻需要花更多時間,因爲每個模塊都需 要以JIT形式加載。在我的應用中,只有當用戶首次在steps 1-5之間切換時需要花更多時間。 
5. 推遲實例化 
   Heider圍繞着Flex組件的“creationPolicy”屬性及何時實例化應用的不同部分給出了很多建議。
   如果你想減少從數據下載到用戶真正可以使用的總時間,當務之急就是推遲實例化。這項技術背後的理念就是直到應用真正使用的時候纔在內存中創建對象
   儘管推遲實例化技術會在應用的整個使用過程中導致少許——通常不那麼明顯——的延遲,但與長時間的啓動延遲相比,它還是可接受的。推遲實例化的另一個好處在於內存使用的優化。

以上原則來自Jun Heider在O’Reilly的InsideRIA站點上發表了一篇精彩的文章,該文章就如何加快Flex應用的啓動速度提出了很多建議,以幫助用戶減少看見討厭的“Loading”對話框的出現時間。他深入探討了問題的不同方面,並對每種技術的優勢和劣勢進行了評判。Heider還談到了一個“實驗性”的條款——“使用流”,這是他在討論Dirk Eismann的帖子(Building monolithic Flex SWFs that still startup quickly.”)時談及的。Eismann提出一項技術以利用Flash Player中的多個frames以在部分應用中達到流的目的。查看所有的帖子以更多地瞭解該技術及關於加快Flex啓動速度的建議。

內存釋放優化原則
1. 被刪除對象在外部的所有引用一定要被刪除乾淨才能被系統當成垃圾回收處理掉;
2. 父對象內部的子對象被外部其他對象引用了,會導致此子對象不會被刪除,子對象不會被刪除又會導致了父對象不會被刪除;
3. 如果一個對象中引用了外部對象,當自己被刪除或者不需要使用此引用對象時,一定要記得把此對象的引用設置爲null;
4. 本對象刪除不了的原因不一定是自己被引用了,也有可能是自己的孩子被外部引用了,孩子刪不掉導致父親也刪不掉;
5. 除了引用需要刪除外,系統組件或者全局工具、管理類如果提供了卸載方法的就一定要調用刪除內部對象,否則有可能會造成內存泄露和性能損失;
6. 父對象立刻被刪除了不代表子對象就會被刪除或立刻被刪除,可能會在後期被系統自動刪除或第二次移除操作時被刪除;
7. 如果父對象remove了子對象後沒有清除對子對象的引用,子對象一樣是不能被刪除的,父對象也不能被刪除;
8. 註冊的事件如果沒有被移除不影響自定義的強行回收機制,但有可能會影響正常的回收機制,所以最好是做到註冊的事件監聽器都要記得移除乾淨。
9. 父對象被刪除了不代表其餘子對象都刪除了,找到一種狀態的泄露代碼不等於其他狀態就沒有泄露了,要各模塊各狀態逐個進行測試分析,直到測試任何狀態下都能刪除整個對象爲止。
內存泄露舉例:
1. 引用泄露:對子對象的引用,外部對本對象或子對象的引用都需要置null;
2. 系統類泄露:使用了系統類而忘記做刪除操作了,如BindingUtils.bindSetter(),ChangeWatcher.watch()函數 時候完畢後需要調用ChangeWatcher.unwatch()函數來清除引用 ,否則使用此函數的對象將不會被刪除;
類似的還有MUSIC,VIDEO,IMAGE,TIMER,EVENT,BINDING等。
3. 效果泄露:當對組件應用效果Effect的時候,當本對象本刪除時需要把本對象和子對象上的Effect動畫停止掉,然後把Effect的target對象置 null; 如果不停止掉動畫直接把 Effect置null將不能正常移除對象。
4. SWF泄露:要完全刪除一個SWF要調用它的unload()方法並且把對象置null;
5. 圖片泄露:當Image對象使用完畢後要把source置null;(爲測試);
6. 聲音、視頻泄露: 當不需要一個音樂或視頻是需要停止音樂,刪除對象,引用置null;
內存泄露解決方法:
1. 在組件的REMOVED_FROM_STAGE事件回掉中做垃圾處理操作(移除所有對外引用(不管是VO還是組件的都需要刪除),刪除監聽器,調用系統類的清除方法)
先remove再置null, 確保被remove或者removeAll後的對象在外部的引用全部釋放乾淨;
2. 利用Flex的性能優化工具Profile來對項目進程進行監控,可知道歷史創建過哪些對象,目前有哪些對象沒有被刪除,創建的數量,佔用的內存比例和用量,創建過程等信息;
總結:關鍵還是要做好清除工作,自己設置的引用自己要記得刪除,自己用過的系統類要記得做好回收處理工作。 以上問題解決的好的話不需要自定義強制回收器也有可能被系統正常的自動回收掉。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章