將 CPU 使用率減至最低

最佳化的另一項重點是 CPU 使用率。最佳化 CPU 處理可改善效能,而行動裝置的電池壽命也會因而延長。

Flash Player 10.1 針對 CPU 使用率的增強功能

Flash Player 10.1 引進兩項新功能,協助節省 CPU 處理。這些功能在 SWF 內容移出螢幕外時,會暫停並繼續內容,並限制頁面上 Flash Player 實體的數目。

暫停、節流和繼續

備註: 暫停、節流和繼續功能不適用於 Adobe® AIR® 應用程式。

為了最佳化 CPU 和電池用量,Flash Player 10.1 會引進與非作用中實體相關的新功能。隨著內容出現在螢幕外或螢幕內,此功能可藉由暫停和繼續 SWF 檔案來限制 CPU 使用率。透過這項功能,Flash Player 會在繼續內容的播放時,移除任何可重新建立的物件,以盡可能釋放記憶體。當整個內容在螢幕外時,會將內容視為在螢幕外。

下列兩個情況都會視為 SWF 內容在螢幕外:

  • 使用者捲動頁面並造成 SWF 內容移到螢幕外。

    在此情況下,如果正在播放任何音效或視訊,內容會繼續播放,但是顯示會停止。如果沒有播放音效或視訊,為了確保播放或 ActionScript 執行不會暫停,請將 hasPriority HTML 參數設定為 True。不過,請記住當內容在螢幕外或隱藏時,會暫停 SWF 內容顯示,不論 hasPriority HTML 參數的值為何。

  • 在瀏覽器中開啟索引標籤,使得 SWF 內容移到背景。

    在此情況下,不論 hasPriority HTML 標籤值為何,SWF 內容都會降低或「節流」至 2 到 8 fps 之間。音效與視訊播放會停止,並且不會處理任何內容顯示,直到 SWF 內容變成可見為止。

如果是 Windows 和 Mac 桌上型電腦瀏覽器上執行的 Flash Player 11.2 及更新的版本,您可以在應用程式中使用 ThrottleEvent。當 Flash Player 暫停、節流或繼續播放時,Flash Player 會傳送 ThrottleEvent。

ThrottleEvent 是廣播事件,表示所有具有針對此事件註冊之偵聽程式的 EventDispatcher 物件都會加以傳送。如需有關廣播事件的詳細資訊,請參閱 DisplayObject 類別。

實體管理

備註: 實體管理功能不適用於 Adobe® AIR® 應用程式。
使用 hasPriority HTML 參數以延遲螢幕外 SWF 檔案的載入。

Flash Player 10.1 引進名為 hasPriority 的新 HTML 參數:

<param name="hasPriority" value="true" />

此功能會限制頁面上啟動的 Flash Player 實體數目。限制實體的數目有助於節省 CPU 和電池資源。這項概念是將特定的優先順序指定給 SWF 內容,以提供某些內容高於頁面上其他內容的優先順序。假設一個簡單的情況:使用者正在瀏覽網站,索引頁裝載三個不同的 SWF 檔案。其中一個是可見的,另一個則是在螢幕上部分可見,而最後一個則是在螢幕外,需要捲動。前兩個動畫會正常啟動,但是最後一個則會延遲,直到它變成可見為止。當 hasPriority 參數不存在或設定為 false 時,預設行為即如此案例所示。若要確保會啟動 SWF 檔案 (即使該檔案在螢幕外),請將 hasPriority 參數設定為 true 。不過,無論 hasPriority 參數的值為何,一律會暫停顯示使用者看不見的 SWF 檔案。

備註: 如果可用的 CPU 資源變低,則不會再自動啟動 Flash Player 實體,即使 hasPriority 參數設定為 true 亦然。如果新實體是載入頁面後透過 JavaScript 所建立,則這些實體將會忽略 hasPriority 旗標。當網站管理者未包含 hasPriority 旗標時,會啟動任何 1x1 像素或 0x0 像素內容,以避免延遲輔助 SWF 檔案的啟動。無論如何,您仍然可以按一下 SWF 檔案來啟動它。此行為方式稱為「按一下播放」。

下列圖表顯示將 hasPriority 參數設為不同值的效果:

不同 hasPriority 參數值的效果

不同 hasPriority 參數值的效果

睡眠模式

Flash Player 10.1 與 AIR 2.5 引進行動裝置的新功能,以協助節省 CPU 處理,也因此可獲得更長的電池壽命。這項功能包括許多行動裝置上使用的背光照明。例如,如果執行行動應用程式的使用者被打斷並停止使用裝置,執行階段會偵測背光照明進入睡眠模式的時機。它接著會將影格速率降到每秒 4 個影格,並暫停顯示。對於 AIR 應用程式,當應用程式移到背景時,睡眠模式也會開始。

ActionScript 程式碼會繼續以睡眠模式執行, 類似將 Stage.frameRate 屬性設定為 4 fps。但是會略過顯示步驟,因此播放程式以 4 fps 執行時,使用者將無法觀看。選擇 4 fps 的影格速率,而不是零,因為它會讓所有的連線維持在開啟的狀態 (NetStream、Socket 與 NetConnection)。切換至零將會中斷開啟的連線。選擇 250 毫秒的重新整理速率 (4 fps),因為許多裝置製造商都會使用這個影格速率做為其重新整理速率。使用這個值會讓執行階段的影格速率保持在與裝置本身大致相同的速率。

備註: 執行階段進入睡眠模式時, Stage.frameRate 屬性會回到原始 SWF 檔案的影格速率,而不再是 4 fps。

當背光照明回到開啟模式時,會繼續顯示。影格速率會返回其原始值。假設使用者正在使用媒體播放應用程式播放音樂。如果螢幕進入睡眠模式,執行階段會根據播放的內容類型來回應。以下是狀況與執行階段行為的對應清單:

  • 背光照明進入睡眠模式,而且正在播放非 A/V 內容:會暫停顯示而且影格速率會設定為 4 fps。

  • 背光照明進入睡眠模式,而且正在播放 A/V 內容:執行階段會強制背光照明永遠開啟,以繼續使用者體驗。

  • 背光照明從睡眠模式進入開啟模式:執行階段將影格速率設定為原始 SWF 檔案影格速率的設定,並繼續顯示。

  • 播放 A/V 內容時暫停 Flash Player:Flash Player 將背光照明狀態重設為預設系統行為,因為 A/V 不再繼續播放。

  • 在播放 A/V 內容時,行動裝置有來電:會暫停顯示而且影格速率會設定為 4 fps。

  • 行動裝置停用背光照明睡眠模式:執行階段會正常運作。

當背光照明進入睡眠模式時,顯示會暫停並且影格速率會變慢。這項功能可節省 CPU 處理,但是無法依賴它來建立真正的暫停,如同遊戲應用程式一般。

備註: 執行階段進入或離開睡眠模式時,不會傳送任何 ActionScript 事件。

凍結與取消凍結物件

使用 REMOVED_FROM_STAGE ADDED_TO_STAGE 事件即可正確地凍結和取消凍結物件。

若要將程式碼最佳化,請凍結和取消凍結您的物件。凍結和取消凍結對於所有物件都很重要,但是對顯示物件尤為重要。即使顯示物件已不再顯示清單中,並等待記憶體回收,它們可能仍然正在使用消耗大量 CPU 的程式碼。例如,它們可能仍然持續使用 Event.ENTER_FRAME。因此,使用 Event.REMOVED_FROM_STAGE Event.ADDED_TO_STAGE 事件正確地凍結與取消凍結物件非常重要。下列範例顯示在舞台中播放並與鍵盤互動的影片片段:

// Listen to keyboard events 
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyIsDown); 
stage.addEventListener(KeyboardEvent.KEY_UP, keyIsUp); 
  
// Create object to store key states 
var keys:Dictionary = new Dictionary(true); 
  
function keyIsDown(e:KeyboardEvent):void 
{ 
    // Remember that the key was pressed 
    keys[e.keyCode] = true;     
  
    if (e.keyCode==Keyboard.LEFT || e.keyCode==Keyboard.RIGHT) 
    { 
        runningBoy.play(); 
    } 
} 
  
function keyIsUp(e:KeyboardEvent):void 
{ 
    // Remember that the key was released 
    keys[e.keyCode] = false; 
  
    for each (var value:Boolean in keys) 
          if ( value ) return; 
    runningBoy.stop(); 
} 
  
runningBoy.addEventListener(Event.ENTER_FRAME, handleMovement); 
runningBoy.stop(); 
  
var currentState:Number = runningBoy.scaleX; 
var speed:Number = 15; 
  
function handleMovement(e:Event):void 
{ 
    if (keys[Keyboard.RIGHT]) 
    { 
        e.currentTarget.x += speed; 
        e.currentTarget.scaleX = currentState;     
    } else if (keys[Keyboard.LEFT]) 
    { 
        e.currentTarget.x -= speed; 
        e.currentTarget.scaleX = -currentState; 
    } 
}

檢視完整大小的圖形
與鍵盤互動的影片片段

按一下「Remove」按鈕時,會從顯示清單移除影片片段:

// Show or remove running boy 
showBtn.addEventListener (MouseEvent.CLICK,showIt); 
removeBtn.addEventListener (MouseEvent.CLICK,removeIt); 
 
function showIt (e:MouseEvent):void 
{ 
    addChild (runningBoy); 
} 
 
function removeIt(e:MouseEvent):void 
{ 
    if (contains(runningBoy)) removeChild(runningBoy); 
}

即使已從顯示清單移除影片片段,影片片段仍然會傳送 Event.ENTER_FRAME 事件。影片片段仍然會執行,但是不會顯示。若要正確處理此情形,請偵聽正確的事件並移除事件偵聽程式,避免執行耗用 CPU 的程式碼:

// Listen to Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE 
runningBoy.addEventListener(Event.ADDED_TO_STAGE,activate); 
runningBoy.addEventListener(Event.REMOVED_FROM_STAGE,deactivate); 
 
function activate(e:Event):void 
{ 
    // Restart everything 
    e.currentTarget.addEventListener(Event.ENTER_FRAME,handleMovement); 
} 
 
function deactivate(e:Event):void 
{ 
    // Freeze the running boy - consumes fewer CPU resources when not shown 
    e.currentTarget.removeEventListener(Event.ENTER_FRAME,handleMovement); 
    e.currentTarget.stop(); 
}

按下「Show」按鈕時,會重新啟動影片片段並再次監聽 Event.ENTER_FRAME 事件,而鍵盤會正確地控制影片片段。

備註: 如果從顯示清單移除顯示物件,並在移除之後將其參考設定為 null ,這樣並無法保證物件已被凍結。如果垃圾回收器未執行,即使不再顯示物件,該物件仍會繼續耗用記憶體與 CPU 處理。若要確保物件盡可能耗用最少的 CPU 處理,請確定物件從顯示清單移除後已被完全凍結。

從 Flash Player 10 與 AIR 1.5 開始,也會發生下列行為。如果播放磁頭遇到空白影格,就會自動凍結顯示物件,即使您未實作任何凍結行為。

使用 Loader 類別來載入遠端內容時,凍結的概念也非常重要。透過 Flash Player 9 與 AIR 1.0 使用 Loader 類別時,必須手動凍結內容,方法是偵聽 LoaderInfo 物件傳送的 Event.UNLOAD 事件。每個物件都必須手動凍結,這是不可忽略的工作。Flash Player 10 與 AIR 1.5 引進 Loader 類別的重要新方法,稱為 unloadAndStop() 。此方法可讓您取消載入 SWF 檔案、自動凍結已載入 SWF 檔案中的每個物件,以及強制執行垃圾回收器。

下列程式碼會載入 SWF 檔案,然後使用 unload() 方法來取消載入 SWF 檔案,但這將需要更多的處理與手動凍結:

var loader:Loader = new Loader(); 
 
loader.load ( new URLRequest ( "content.swf" ) ); 
 
addChild ( loader ); 
 
stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
 
function unloadSWF ( e:MouseEvent ):void 
{ 
    // Unload the SWF file with no automatic object deactivation 
    // All deactivation must be processed manually 
    loader.unload(); 
}

最好的方法是使用 unloadAndStop() 方法,這會以原生方式處理凍結,並強制執行記憶體回收程序:

var loader:Loader = new Loader(); 
 
loader.load ( new URLRequest ( "content.swf" ) ); 
 
addChild ( loader ); 
 
stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
 
function unloadSWF ( e:MouseEvent ):void 
{ 
    // Unload the SWF file with automatic object deactivation 
    // All deactivation is handled automatically 
    loader.unloadAndStop(); 
}

呼叫 unloadAndStop() 方法後,會發生下列動作:

  • 聲音停止。

  • 移除向 SWF 檔案的主要時間軸註冊的偵聽程式。

  • Timer 物件停止。

  • 硬體週邊裝置 (例如攝影機與麥克風) 已釋出。

  • 每個影片片段都會停止。

  • Event.ENTER_FRAME Event.FRAME_CONSTRUCTED Event.EXIT_FRAME Event.ACTIVATE Event.DEACTIVATE 停止傳送。

啟用和停用事件

使用 Event.ACTIVATE Event.DEACTIVATE 事件以偵測背景的不作用狀態,並適當地最佳化您的應用程式。

這兩個事件 ( Event.ACTIVATE Event.DEACTIVATE ) 可以協助您微調應用程式,使其盡可能使用最少的 CPU 週期。這些事件可讓您偵測執行階段獲得或失去焦點。因此,可以最佳化程式碼以反應內容變更。下列程式碼會同時監聽這兩個事件,並在應用程式失去其焦點時,將影格速率動態地變更為零。例如,當使用者切換到其他索引標籤或將應用程式放入背景時,動畫會失去焦點:

var originalFrameRate:uint = stage.frameRate; 
var standbyFrameRate:uint = 0; 
  
stage.addEventListener ( Event.ACTIVATE, onActivate ); 
stage.addEventListener ( Event.DEACTIVATE, onDeactivate ); 
  
function onActivate ( e:Event ):void 
{ 
    // restore original frame rate 
    stage.frameRate = originalFrameRate; 
} 
  
function onDeactivate ( e:Event ):void 
{ 
    // set frame rate to 0 
    stage.frameRate = standbyFrameRate; 
}

當應用程式再次獲得焦點時,會將影格速率重設為其原始值。您也可以考慮進行其他最佳化,例如凍結和取消凍結物件,以取代動態變更影格速率。

啟用和停用事件可讓您實作類似行動裝置與筆記型電腦上的「暫停和繼續」功能。

滑鼠互動

盡可能考慮停用滑鼠互動。

使用互動式物件 (例如 MovieClip 或 Sprite 物件) 時,執行階段會執行原生程式碼來偵測與處理滑鼠互動。當螢幕上有許多互動式物件的時候 (尤其是當這些物件重疊時),偵測滑鼠互動可能會耗用大量 CPU 資源。避免此處理最簡單的方法,就是在不需要滑鼠互動的物件中停用滑鼠互動。下列程式碼說明 mouseEnabled mouseChildren 屬性的用法:

// Disable any mouse interaction with this InteractiveObject 
myInteractiveObject.mouseEnabled = false; 
const MAX_NUM:int = 10; 
  
// Create a container for the InteractiveObjects 
var container:Sprite = new Sprite(); 
  
for ( var i:int = 0; i< MAX_NUM; i++ ) 
{ 
    // Add InteractiveObject to the container 
    container.addChild( new Sprite() ); 
} 
  
// Disable any mouse interaction on all the children 
container.mouseChildren = false;

盡可能考慮停用滑鼠互動,這樣有助於應用程式使用較少的 CPU 處理,進而降低行動裝置的電池用量。

計時器與 ENTER_FRAME 事件的比較

視內容是否為動畫而定,選擇計時器或 ENTER_FRAME 事件。

對於長時間執行的非動畫內容而言,使用計時計會比 Event.ENTER_FRAME 事件來得好。

在 ActionScript 3.0 中,有兩種方式可在特定間隔呼叫函數。第一種方式是使用顯示物件 (DisplayObject) 傳送的 Event.ENTER_FRAME 事件。第二種方式是使用計時器。ActionScript 開發人員經常會用到 ENTER_FRAME 事件。 ENTER_FRAME 事件會在每個影格中加以傳送。因此,呼叫函數的間隔與目前影格速率是相關的。影格速率會透過 Stage.frameRate 屬性來存取。不過在有些情況下,使用計時器可能會比使用 ENTER_FRAME 事件來得好。例如,如果您不使用動畫,但想要在特定間隔呼叫您的程式碼,則使用計時器會是較佳的選擇。

計時器的行為方式和 ENTER_FRAME 事件相似,但計時器傳送事件時不受影格速率影響。此行為可提供相當程度的最佳化。以視訊播放程式應用程式為例。在此情形下,您不需要使用高影格速率,因為只需移動應用程式控制項。

備註: 影格速率不會影響視訊,因為該視訊不是內嵌在時間軸內。視訊是透過漸進式下載或串流載入的。

在此範例中,影格速率設為較低的值 10 fps。計時器以每秒更新一次的速率來更新控制項。較高的更新速率亦可藉由 updateAfterEvent() 方法 (位於 TimerEvent 物件) 取得。如有必要,此方法可強制畫面在每次計時器傳送事件時更新。下列程式碼說明此概念:

// Use a low frame rate for the application 
stage.frameRate = 10; 
  
// Choose one update per second 
var updateInterval:int = 1000; 
var myTimer:Timer = new Timer(updateInterval,0); 
  
myTimer.start(); 
myTimer.addEventListener( TimerEvent.TIMER, updateControls ); 
  
function updateControls( e:TimerEvent ):void 
{ 
    // Update controls here 
    // Force the controls to be updated on screen 
    e.updateAfterEvent(); 
}

呼叫 updateAfterEvent() 方法不會修改影格速率。只會強制執行階段更新畫面上已變更的內容。時間軸仍是以 10 fps 執行。請記住,在低效能裝置或當事件處理常式包含需要密集處理的程式碼時,計時器和 ENTER_FRAME 事件並不一定會完全準確。就像 SWF 檔案影格速率,計時器的更新影格速率在不同情況下也會有所不同。

讓應用程式中的 Timer 物件數目與註冊的 enterFrame 處理常式數目減至最少。

執行階段會針對每個影格將 enterFrame 事件傳送給顯示清單中的每個顯示物件。雖然您可以針對多個顯示物件註冊 enterFrame 事件的偵聽程式,但這樣做表示每個影格需執行更多的程式碼。請考慮改用單一集中的 enterFrame 處理常式,該處理常式會執行在每個影格上執行的所有程式碼。透過集中此程式碼,您可以輕鬆地管理所有經常執行的程式碼。

同樣地,如果您使用 Timer 物件,則從多個 Timer 物件建立和傳送事件會產生相關的額外負荷。如果您在不同的間隔觸發不同的作業,以下是一些建議的替代方案:

  • 盡可能根據發生的頻率,使用最少的 Timer 物件與群組作業。

    例如,對頻繁的作業使用一個 Timer,設定為每隔 100 毫秒觸發一次。對較不頻繁的作業或背景作業使用另一個 Timer,設定為每隔 2000 毫秒觸發一次。

  • 使用單一 Timer 物件,並以多個 Timer 物件的 delay 屬性間隔觸發作業。

    例如,假設您要讓某些作業每隔 100 毫秒執行一次,並讓其他作業每隔 200 毫秒執行一次。在該情況下,您可以使用 delay 值為 100 毫秒的單一 Timer 物件。在 timer 事件處理常式中加入條件陳述式,如此一來,每隔一次才會執行 200 毫秒的作業。下列範例將示範這種技巧:

    var timer:Timer = new Timer(100); 
    timer.addEventListener(TimerEvent.Timer, timerHandler); 
    timer.start(); 
         
    var offCycle:Boolean = true; 
      
    function timerHandler(event:TimerEvent):void 
    { 
        // Do things that happen every 100 ms 
         
        if (!offCycle) 
        { 
            // Do things that happen every 200 ms 
        } 
         
        offCycle = !offCycle; 
    }
不使用 Timer 物件時,請停用 Timer 物件。

如果 Timer 物件的 timer 事件處理常式只會在特定條件下執行作業,則當所有條件都不成立時,可以呼叫 Timer 物件的 stop() 方法。

enterFrame 事件或 Timer 處理常式中,變更顯示物件的外觀會導致重新繪製螢幕,所以請將變更次數減至最少。

每個影格的顯示階段都會重新繪製於該影格期間所有變更的舞台部分。如果需要重新繪製的區域很大,或區域很小但包含大量或複雜的顯示物件,則執行階段會需要較長的時間來進行顯示處理。若要測試所需的重新繪製量,請在 Flash Player 除錯程式或 AIR 中,使用「顯示重新繪製區域」功能。

如需有關改善重複動作效能的詳細資訊,請參閱下列文章:

補間動畫行為模式

若要節省 CPU 電力,請限制補間動畫的使用,這樣可以節省 CPU 處理、記憶體以及電池壽命。

設計人員和開發人員在桌上型電腦上製作 Flash 內容時,通常會在應用程式中使用許多移動補間動畫。在低效能的行動裝置上製作內容時,請嘗試將移動補間動畫的使用率降至最低。如此有助於內容在低階裝置上較快速地執行。