套件 | flash.system |
類別 | public final class Worker |
繼承 | Worker EventDispatcher Object |
語言版本: | ActionScript 3.0 |
執行階段版本: | Flash Player 11.4, AIR 3.4 |
Worker 可讓您在另一個 Worker 正在執行其他作業 (包括主要 SWF Worker) 時,同時「在背景」執行程式碼。在非 Worker 的內容中,有些諸如在迴圈中處理大量資料的作業會花費相當長的時間來執行,以防止主應用程式執行緒以夠快的速度更新畫面。這會導致畫面出現斷續或凍結的情形。
使用 Worker 可讓您在背景執行需長時間執行或緩慢的作業。每個 Worker 會在有別於其他 Worker 的個別執行緒中執行程式碼。一個 Worker 中需長時間執行的程式碼,並不會使另一個 Worker 中的程式碼無法執行。相反地,兩組程式碼會平行執行。因此,Worker 可用來在背景中執行程式碼,而主應用程式執行緒會維持空閒狀態,以便繼續更新畫面。
這個同時執行多組程式碼指示的能力,又稱為「並行」。
注意:桌面平台的 Flash Player 和 AIR 支援將 Worker 做為並行之用。對於行動平台,Android 和 iOS 上的 AIR 皆支援並行。您可以在嘗試使用之前,先使用靜態 isSupported 屬性來確認是否支援並行。
您無法透過呼叫 Worker()
建構函式,直接建立 Worker 實體。在支援將 Worker 做為並行之用的內容中,執行階段會自動在啟動時建立與主要 SWF 相關聯的 Worker,又稱為「原始 Worker」。
每個額外的 Worker 都是透過個別 SWF 來建立。若要建立 Worker 類別的新實體,請以背景 Worker 的 SWF 位元組做為引數,將 ByteArray 傳遞至 WorkerDomain 類別的 createWorker()
方法。有三個常用方法可存取 SWF 的位元組用於此用途:
-
使用 [Embed] 中繼標籤,可將 .swf 檔案當做 ByteArray 內嵌於應用程式中:
// Embed the SWF file [Embed(source="../swfs/BgWorker.swf", mimeType="application/octet-stream")] private static var BgWorker_ByteClass:Class; private function createWorker():void { // create the background worker var workerBytes:ByteArray = new BgWorker_ByteClass(); var bgWorker:Worker = WorkerDomain.current.createWorker(workerBytes); // listen for worker state changes to know when the worker is running bgWorker.addEventListener(Event.WORKER_STATE, workerStateHandler); // set up communication between workers using // setSharedProperty(), createMessageChannel(), etc. // ... (not shown) bgWorker.start(); }
-
使用 URLLoader 載入外部 SWF 檔案:
// load the SWF file var workerLoader:URLLoader = new URLLoader(); workerLoader.dataFormat = URLLoaderDataFormat.BINARY; workerLoader.addEventListener(Event.COMPLETE, loadComplete); workerLoader.load(new URLRequest("BgWorker.swf")); private function loadComplete(event:Event):void { // create the background worker var workerBytes:ByteArray = event.target.data as ByteArray; var bgWorker:Worker = WorkerDomain.current.createWorker(workerBytes); // listen for worker state changes to know when the worker is running bgWorker.addEventListener(Event.WORKER_STATE, workerStateHandler); // set up communication between workers using // setSharedProperty(), createMessageChannel(), etc. // ... (not shown) bgWorker.start(); }
-
將單一 SWF 當做原始 Worker 和背景 Worker 之用:
// The primordial worker's main class constructor public function PrimordialWorkerClass() { init(); } private function init():void { var swfBytes:ByteArray = this.loaderInfo.bytes; // Check to see if this is the primordial worker if (Worker.current.isPrimordial) { // create a background worker var bgWorker:Worker = WorkerDomain.current.createWorker(swfBytes); // listen for worker state changes to know when the worker is running bgWorker.addEventListener(Event.WORKER_STATE, workerStateHandler); // set up communication between workers using // setSharedProperty(), createMessageChannel(), etc. // ... (not shown) bgWorker.start(); } else // entry point for the background worker { // set up communication between workers using getSharedProperty() // ... (not shown) // start the background work } }
Worker 會以彼此隔離的方式執行,並且無法存取相同的記憶體、變數和程式碼。不過,您可以使用三種機制,在 Worker 實體之間傳遞訊息和資料:
- 共用屬性:每個 Worker 都有一組內部具名值,這組值可以從 Worker 本身以及其他 Worker 進行設定並讀取。您可以使用
setSharedProperty()
方法來設定值,並使用getSharedProperty()
方法來讀取值。 - MessageChannel:MessageChannel 物件可讓您將單向訊息和資料從一個 Worker 傳送到另一個 Worker。接收端 Worker 中的程式碼會偵聽訊息送達時要通知的事件。若要建立 MessageChannel 物件,請使用
createMessageChannel()
方法。 - 可共用的 ByteArray:如果 ByteArray 物件的
shareable
屬性為true
,則所有 Worker 中的該 ByteArray 實體都會使用相同的基礎記憶體。由於多個 Worker 中的程式碼可以同時存取共用記憶體,因此,您的程式碼應使用ByteArray.shareable
屬性說明中所述的機制,以避免發生未預期的資料變更問題。
在背景 Worker 中執行的程式碼無法使用幾個執行階段 API。這些主要由與使用者輸入和輸出機制相關的 API,或如視窗及拖曳等作業系統元素所組成。根據規定,對於所有內容中不支援的任何 API,請在嘗試使用 API 之前,先使用 isSupported
、available
和類似的屬性來檢查在背景 Worker 內容中是否可以使用 API。
注意:背景和次要 Worker 不支援原生擴充功能。
Worker 很實用,因為它降低了影格速率因主要顯示執行緒遭到其他程式碼封鎖而丟棄的可能性。不過,Worker 需要額外的系統記憶體和 CPU 使用量,這會讓整體應用程式效能付出不少代價。由於每個 Worker 都會使用專屬的執行階段虛擬機器實體,因此,即使是簡單的 Worker,工作負荷也會很大。在使用 Worker 時,請在所有目標平台間測試您的程式碼,以確保系統上的需求不會太大。Adobe 建議在一般情況下,不要使用超過一個或兩個背景 Worker。
詳細資訊
AS3 Worker 簡介:影像處理,作者 Shawn Blais
多執行緒物理:將 AS3 Worker 用於物理引擎,作者 Shawn Blais
相關 API 元素
屬性 | 定義自 | ||
---|---|---|---|
constructor : Object
類別物件的參照或是特定物件實體的建構函數。 | Object | ||
current : Worker [靜態] [唯讀]
提供存取包含目前程式碼的 Worker
| Worker | ||
isPrimordial : Boolean [唯讀]
指出這個 Worker 是否為原始 Worker。 | Worker | ||
isSupported : Boolean [靜態] [唯讀]
指出目前的執行階段內容是否支援使用 Worker 物件以便同時執行程式碼。 | Worker | ||
state : String [唯讀]
Worker 在其效期內的目前狀態。 | Worker |
方法 | 定義自 | ||
---|---|---|---|
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void [覆寫]
會在 EventDispatcher 物件註冊事件偵聽程式,以便讓偵聽程式收到事件的通知。 | Worker | ||
建立新的 MessageChannel 實體,從 Worker 將呼叫方法的訊息傳送至其他接收端 Worker。 | Worker | ||
會將事件傳送到事件流程。 | EventDispatcher | ||
擷取以具名索引鍵儲存在這個 Worker 中的值。 | Worker | ||
會檢查 EventDispatcher 物件是否有對特定的事件類型註冊偵聽程式。 | EventDispatcher | ||
指出物件是否有已定義的指定屬性。 | Object | ||
指出 Object 類別的實體是否位於指定為參數的物件原型鏈中。 | Object | ||
指出指定的屬性是否存在,以及是否可列舉。 | Object | ||
[覆寫]
會從 EventDispatcher 物件移除偵聽程式。 | Worker | ||
為迴圈作業設定動態屬性的可用性。 | Object | ||
提供 Worker SWF 中執行的程式碼可以使用的具名值。 | Worker | ||
開始執行 Worker。 | Worker | ||
停止執行這個 Worker 的程式碼。 | Worker | ||
傳回代表此物件的字串,根據地區特定慣例進行格式化。 | Object | ||
會傳回指定之物件的字串形式。 | Object | ||
會傳回指定之物件的基本值。 | Object | ||
檢查此 EventDispatcher 物件是否已註冊事件偵聽程式,或者此物件的任何祖系已為特定事件類型註冊事件偵聽程式。 | EventDispatcher |
事件 | 摘要 | 定義自 | ||
---|---|---|---|---|
[廣播事件] 當 Flash Player 或 AIR 應用程式取得作業系統焦點並成為作用中時傳送。 | EventDispatcher | |||
[廣播事件] 當 Flash Player 或 AIR 應用程式失去作業系統焦點並成為非作用中時傳送。 | EventDispatcher | |||
當 Worker 的 state 屬性值變更時傳送。 | Worker |
current | 屬性 |
isPrimordial | 屬性 |
isPrimordial:Boolean
[唯讀] 語言版本: | ActionScript 3.0 |
執行階段版本: | Flash Player 11.4, AIR 3.4 |
指出這個 Worker 是否為原始 Worker。
「原始 Worker」是初始 SWF 執行所在的 Worker。這個 Worker 會控制畫面的顯示。
這個屬性可用來建構應用程式,在應用程式中,原始 Worker 和背景 Worker 是相同 SWF 檔案的兩個實體。另一個方法是建構您的程式碼,使背景 Worker 使用不同於原始 Worker 的程式碼,以編譯為不同的 SWF。
實作
public function get isPrimordial():Boolean
isSupported | 屬性 |
state | 屬性 |
addEventListener | () | 方法 |
override public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
語言版本: | ActionScript 3.0 |
執行階段版本: | Flash Player 11.4, AIR 3.4 |
會在 EventDispatcher 物件註冊事件偵聽程式,以便讓偵聽程式收到事件的通知。您可以在顯示清單上的所有節點,為特定類型的事件、階段與優先順序註冊事件偵聽程式。
當您成功註冊事件偵聽程式後,就不可以透過對 addEventListener()
的額外呼叫來變更其優先順序。 若要變更偵聽程式的優先順序,您必須先呼叫 removeListener()
。 然後,您就可以使用新的優先順序等級來重新註冊偵聽程式。
請記住,一旦您註冊了偵聽程式,對 addEventListener()
進行後續呼叫,若使用不同的 type
或 useCapture
值,將會導致建立個別的偵聽程式註冊程序。 例如,如果您先將 useCapture
設為 true
來註冊偵聽程式,則只會在捕捉階段進行偵聽。 如果您使用相同的偵聽程式物件,再次呼叫 addEventListener()
(但將 useCapture
設為 false
) 會得到兩個不同的偵聽程式: 一個會在捕捉階段進行偵聽,另一個則會在目標與反昇階段進行偵聽。
您無法單獨針對目標階段或反昇階段註冊事件偵聽程式。 由於反昇階段只適用於目標節點的祖系,因此這兩種階段必須一起進行註冊。
如果您不再需要事件偵聽程式,可以呼叫 removeEventListener()
來移除它,否則可能產生記憶體問題。 事件偵聽程式不會自動從記憶體中移除,因為只要傳送的物件存在,垃圾回收器就不會移除偵聽程式 (除非 useWeakReference
參數設定為 true
)。
複製 EventDispatcher 實體並不會將附加在該實體上的事件偵聽程式一併複製 (如果新建立的節點需要事件偵聽程式,則您必須在建立節點後再附加偵聽程式)。 然而,如果您移動了 EventDispatcher 實體,則附加的事件偵聽程式也會跟著移動。
若於此節點正在處理事件的同時將事件偵聽程式註冊到某個節點上,則事件偵聽程式不會在目前階段觸發,但會在事件流程的後續階段 (例如反昇階段) 中觸發。
如果您在節點正在處理事件的同時將事件偵聽程式從節點中移除,則事件偵聽程式還是會被目前的動作觸發。 一旦移除了事件偵聽程式,就無法再叫用它 (除非您為了日後的處理而再次註冊它)。
參數
type:String — 事件類型。
| |
listener:Function — 處理事件的偵聽程式函數。此函數必須接受 Event 物件並當做唯一的參數,而且必須傳回空值,如下列範例所示:
function(evt:Event):void 函數可以具有任何名稱。 | |
useCapture:Boolean (default = false ) —
判斷偵聽程式是否可在捕捉階段或目標與反昇階段運作。如果 useCapture 設為 true ,則偵聽程式只會在捕捉階段 (而不是在目標或反昇階段) 處理事件。如果 useCapture 為 false ,則偵聽程式只會在目標或反昇階段處理事件。若要在全部三個階段中偵聽事件,請呼叫 addEventListener 兩次,先將 useCapture 設為 true 進行第一次呼叫,再將 useCapture 設為 false 進行第二次呼叫。
| |
priority:int (default = 0 ) — 事件偵聽程式的優先順序等級。優先順序是由一個具有正負號的 32 位元整數所指定。 數字越大,代表優先順序越高。所有優先順序為 n 的偵聽程式都會比優先順序為 n -1 的偵聽程式優先處理。 如果有兩個以上的偵聽程式共用同一個優先順序,則會依據加入的先後次序來處理。預設的優先順序為 0。
| |
useWeakReference:Boolean (default = false ) — 判斷偵聽程式的參照為強或弱。強參照 (預設) 會避免偵聽程式被垃圾回收器從記憶體中移除, 弱參照則無法避免這個情況發生。 類別層級的成員函數不受記憶體回收限制,因此您可以將類別層級成員函數的 |
createMessageChannel | () | 方法 |
public function createMessageChannel(receiver:Worker):MessageChannel
語言版本: | ActionScript 3.0 |
執行階段版本: | Flash Player 11.4, AIR 3.4 |
建立新的 MessageChannel 實體,從 Worker 將呼叫方法的訊息傳送至其他接收端 Worker。Worker 中的程式碼 (可建立 MessageChannel 物件) 會使用此方法,將單向訊息傳送到以 receiver
引數指定的 Worker 物件。
雖然 MessageChannel 實體可用來將訊息和資料從一個 Worker 實體傳送到另一個實體,但還是必須呼叫 Worker 物件的 setSharedProperty()
方法,將至少一個 MessageChannel 實體傳遞至子 Worker 做為共用屬性。
outgoingChannel = Worker.current.createMessageChannel(bgWorker); incomingChannel = bgWorker.createMessageChannel(Worker.current); bgWorker.setSharedProperty("incoming", outgoingChannel); bgWorker.setSharedProperty("outgoing", incomingChannel); // listen for messages from the receiving MessageChannel // This event is triggered when the background sends a message to this worker incomingChannel.addEventListener(Event.CHANNEL_MESSAGE, incomingMessageHandler);
參數
receiver:Worker — 將接收透過建立的訊息通道所傳輸之訊息的 Worker
|
MessageChannel — 作業建立的 MessageChannel 物件
|
getSharedProperty | () | 方法 |
removeEventListener | () | 方法 |
override public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false):void
語言版本: | ActionScript 3.0 |
執行階段版本: | Flash Player 11.4, AIR 3.4 |
會從 EventDispatcher 物件移除偵聽程式。 如果沒有相符的偵聽程式以 EventDispatcher 物件來註冊,則呼叫此方法不會產生任何作用。
參數
type:String — 事件類型。
| |
listener:Function — 要移除的偵聽程式物件。
| |
useCapture:Boolean (default = false ) —
指定偵聽程式是否針對捕捉階段或目標與反昇階段而註冊。如果偵聽程式同時針對捕捉階段和目標與反昇階段而註冊,則必須移除對 removeEventListener() 的兩次呼叫 (一次呼叫的 useCapture() 是設為 true ,另一次呼叫的 useCapture() 則是設定為 false ),才能移除這兩個階段。
|
setSharedProperty | () | 方法 |
public function setSharedProperty(key:String, value:*):void
語言版本: | ActionScript 3.0 |
執行階段版本: | Flash Player 11.4, AIR 3.4 |
提供 Worker SWF 中執行的程式碼可以使用的具名值。
在呼叫 Worker 的 start()
方法之前,您可以先呼叫這個方法。在此情況下,Worker SWF 中的程式碼可以在建構時使用共用屬性。
傳遞至 value
參數的值幾乎可以是任何物件。除了下述例外,任何傳遞至 value
參數的物件都不會以傳址方式傳遞。呼叫 setSharedProperty()
之後,對一個 Worker 中的物件所做的任何變更不會轉給其他 Worker。物件會透過序列化為 AMF3 格式並還原序列化為接收端 Worker 中新物件的方式進行複製。因此,任何無法序列化為 AMF3 格式的物件 (包括顯示物件) 都無法傳遞至 value
參數。為了讓自訂類別可以順利傳遞,類別定義必須使用 flash.net.registerClassAlias()
函數或 [RemoteClass]
中繼資料來註冊。無論使用何種技術,類別的兩個 Worker 版本必須使用相同的別名。
有五種物件可排除在物件無法在 Worker 之間共用的規則之外。
- Worker
- MessageChannel
- 可共用的 ByteArray (
shareable
屬性設為true
的 ByteArray 物件) - Mutex
- Condition
如果您將這些物件的實體傳遞至 value
參數,則每個 Worker 會有相同基礎物件的參考。對一個 Worker 中的實體所做的變更會立即出現在另一個 Worker 中。此外,如果您使用 setSharedProperty()
傳遞這些物件的相同實體一次以上,執行階段便無法在接收端 Worker 中建立物件的新副本。反而會重複使用相同參考,以減少系統記憶體使用量。
在 value
引數為 null
或 undefined
的情況下呼叫這個方法時,會清除任何先前為指定之 key
引數設定的值。以這種方式清除值會移除值的參考,以便進行記憶體回收作業。
您可以在 key 引數中使用任何字串值。這些共用屬性可供能存取 Worker 的任何程式碼使用。為避免不慎覆寫值,請考慮使用字首、字尾或類似的機制,嘗試讓您的索引鍵名稱成為唯一的。
參數
key:String — 儲存共用屬性所用的名稱。
| |
value:* — 共用屬性的值。
|
相關 API 元素
start | () | 方法 |
public function start():void
語言版本: | ActionScript 3.0 |
執行階段版本: | Flash Player 11.4, AIR 3.4 |
開始執行 Worker。執行階段會建立 Worker 執行緒並呼叫 Worker SWF 主要類別的建構函式。
這項作業是非同步的。一旦 Worker 啟動完成,就會將它的 state
屬性變更為 WorkerState.RUNNING
並傳送 workerState
事件。
terminate | () | 方法 |
workerState | 事件 |
flash.events.Event
屬性 Event.type =
flash.events.Event.WORKER_STATE
語言版本: | ActionScript 3.0 |
執行階段版本: | Flash Player 11.4, AIR 3.4 |
當 Worker 的 state
屬性值變更時傳送。
Event.WORKER_STATE
常數會定義 workerState
事件物件的 type
屬性值。
這個事件具有下列屬性:
屬性 | 值 |
---|---|
bubbles | false |
cancelable | false ;沒有要取消的預設行為指令。 |
currentTarget | 正主動使用事件偵聽程式處理 Event 物件的物件。 |
target | 傳送此事件的物件。 |
這個範例包含三個 ActionScript 類別:WorkerExample 是主要類別和父 Worker。BackgroundWorker 是執行背景工作的類別。它會編譯為背景 Worker SWF 的主要類別。CountResult 是用來在兩個 Worker 之間傳遞資料做為單一物件 (而非多個值) 的自訂類別。
在這個範例中,背景 Worker 會在迴圈中計數,直到達到父 Worker 所指定的數字。隨著背景 Worker 的工作進展,它會將進度訊息傳送至父 Worker。最後,當計數完成時,背景 Worker 會傳送訊息給父 Worker,通知它計數完成及計數所花費的時間。
WorkerExample 類別是 SWF 的主要類別,因此,它是原始 Worker 的主要類別。在 initialize()
方法中,程式碼會使用 BackgroundWorker 類別的位元組 (透過 [Embed]
標籤嵌入) 來建立背景 Worker 物件。
呼叫 WorkerDomain.createWorker()
建立背景 Worker 之後,程式碼會在 Worker 之間設定通訊。首先,程式碼會建立一組 MessageChannel 物件。透過呼叫它的 setSharedProperty()
方法,將物件傳遞至背景 Worker。最後,註冊背景 Worker 物件的 workerState
事件並透過呼叫它的 start()
方法來啟動 Worker。
隨著背景 Worker 執行工作,它會將進度 (及最終結果) 訊息傳送至父 Worker。父 Worker 會使用這項資訊來更新進度列和文字指示符。
package { import com.adobe.example.vo.CountResult; import flash.display.Shape; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.net.registerClassAlias; import flash.system.MessageChannel; import flash.system.Worker; import flash.system.WorkerDomain; import flash.system.WorkerState; import flash.text.TextField; import flash.text.TextFormat; import flash.text.TextFormatAlign; import flash.utils.ByteArray; public class WorkerExample extends Sprite { // ------- Embed the background worker swf as a ByteArray ------- [Embed(source="../workerswfs/BackgroundWorker.swf", mimeType="application/octet-stream")] private static var BackgroundWorker_ByteClass:Class; public static function get BackgroundWorker():ByteArray { return new BackgroundWorker_ByteClass(); } private var bgWorker:Worker; private var bgWorkerCommandChannel:MessageChannel; private var progressChannel:MessageChannel; private var resultChannel:MessageChannel; public function WorkerExample() { initialize(); } private function initialize():void { // create the user interface setupStage(); createStatusText(); createProgressBar(); // Register the alias so we can pass CountResult objects between workers registerClassAlias("com.adobe.test.vo.CountResult", CountResult); // Create the background worker bgWorker = WorkerDomain.current.createWorker(BackgroundWorker); // Set up the MessageChannels for communication between workers bgWorkerCommandChannel = Worker.current.createMessageChannel(bgWorker); bgWorker.setSharedProperty("incomingCommandChannel", bgWorkerCommandChannel); progressChannel = bgWorker.createMessageChannel(Worker.current); progressChannel.addEventListener(Event.CHANNEL_MESSAGE, handleProgressMessage) bgWorker.setSharedProperty("progressChannel", progressChannel); resultChannel = bgWorker.createMessageChannel(Worker.current); resultChannel.addEventListener(Event.CHANNEL_MESSAGE, handleResultMessage); bgWorker.setSharedProperty("resultChannel", resultChannel); // Start the worker bgWorker.addEventListener(Event.WORKER_STATE, handleBGWorkerStateChange); bgWorker.start(); } private function handleBGWorkerStateChange(event:Event):void { if (bgWorker.state == WorkerState.RUNNING) { _statusText.text = "Background worker started"; bgWorkerCommandChannel.send(["startCount", 100000000]); } } private function handleProgressMessage(event:Event):void { var percentComplete:Number = progressChannel.receive(); setPercentComplete(percentComplete); _statusText.text = Math.round(percentComplete).toString() + "% complete"; } private function handleResultMessage(event:Event):void { var result:CountResult = resultChannel.receive() as CountResult; setPercentComplete(100); _statusText.text = "Counted to " + result.countTarget + " in " + (Math.round(result.countDurationSeconds * 10) / 10) + " seconds"; } // ------- Create UI ------- private var _currentPercentComplete:int = 0; private var _needsValidation:Boolean = false; private var _statusText:TextField; private var _progressBarRect:Shape; private var _progressBar:Shape; private function setupStage():void { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; stage.stageWidth = 800; stage.stageHeight = 600; stage.color = 0xffffff; } private function createStatusText():void { _statusText = new TextField(); _statusText.width = 400; _statusText.height = 25; _statusText.x = (stage.stageWidth - _statusText.width) / 2; _statusText.y = 150; var statusTextFormat:TextFormat = new TextFormat(); statusTextFormat.color = 0xeeeeee; statusTextFormat.font = "Verdana"; statusTextFormat.align = TextFormatAlign.CENTER; statusTextFormat.size = 16; _statusText.defaultTextFormat = statusTextFormat; _statusText.wordWrap = false; _statusText.opaqueBackground = 0x999999; _statusText.selectable = false; _statusText.text = "Initializing..."; addChild(_statusText); } private function createProgressBar():void { _progressBarRect = new Shape(); _progressBarRect.graphics.beginFill(0x000000, 0); _progressBarRect.graphics.lineStyle(2, 0x000000); _progressBarRect.graphics.drawRect(0, 0, 400, 30); _progressBarRect.graphics.endFill(); _progressBarRect.x = (stage.stageWidth - _progressBarRect.width) / 2; _progressBarRect.y = 100; addChild(_progressBarRect); _progressBar = new Shape(); _progressBar.graphics.beginFill(0x0000ee); _progressBar.graphics.drawRect(0, 0, 391, 21); _progressBar.x = _progressBarRect.x + 4; _progressBar.y = _progressBarRect.y + 4; addChild(_progressBar); _progressBar.scaleX = 0; } private function setPercentComplete(percentComplete:int):void { if (_currentPercentComplete == percentComplete) return; _currentPercentComplete = percentComplete; invalidateValue(); } private function invalidateValue():void { if (_needsValidation) return; _needsValidation = true; addEventListener(Event.EXIT_FRAME, validate); } private function validate(event:Event):void { removeEventListener(Event.EXIT_FRAME, validate); _needsValidation = false; _redrawProgressBar(); } private function _redrawProgressBar():void { _progressBar.scaleX = _currentPercentComplete / 100; } } }
在 initialize()
方法中,它會接收父 Worker 傳入所在的 MessageChannel 物件。這些物件可用於兩個 Worker 之間的通訊。
父 Worker 會在 commandChannel
訊息通道上呼叫 send()
方法以傳送訊息。在背景 Worker 內,執行階段接著會呼叫 handleCommandMessage()
方法來傳送 channelMessage
事件。
背景 Worker 的實際工作會在 count()
方法中進行。隨著背景 Worker 進行計數,它會在 progressChannel
MessageChannel 物件上呼叫 send()
方法,藉此將進度訊息傳送至父 Worker。當它完成計數時,會在 resultChannel
MessageChannel 物件上呼叫 send()
方法。
package com.adobe.example.workers { import com.adobe.example.vo.CountResult; import flash.display.Sprite; import flash.events.Event; import flash.net.registerClassAlias; import flash.system.MessageChannel; import flash.system.Worker; import flash.utils.getTimer; public class BackgroundWorker extends Sprite { private var commandChannel:MessageChannel; private var progressChannel:MessageChannel; private var resultChannel:MessageChannel; public function BackgroundWorker() { initialize(); } private function initialize():void { registerClassAlias("com.adobe.test.vo.CountResult", CountResult); // Get the MessageChannel objects to use for communicating between workers // This one is for receiving messages from the parent worker commandChannel = Worker.current.getSharedProperty("incomingCommandChannel") as MessageChannel; commandChannel.addEventListener(Event.CHANNEL_MESSAGE, handleCommandMessage); // These are for sending messages to the parent worker progressChannel = Worker.current.getSharedProperty("progressChannel") as MessageChannel; resultChannel = Worker.current.getSharedProperty("resultChannel") as MessageChannel; } private function handleCommandMessage(event:Event):void { if (!commandChannel.messageAvailable) return; var message:Array = commandChannel.receive() as Array; if (message != null && message[0] == "startCount") { count(uint(message[1])); } } private function count(targetValue:uint):void { var startTime:int = getTimer(); var onePercent:uint = uint(Math.ceil(targetValue / 100)); var oneHalfPercent:Number = onePercent / 2; var i:uint = 0; while (i < targetValue) { i++; // only send progress messages every one-half-percent milestone // to avoid flooding the message channel if (i % oneHalfPercent == 0) { progressChannel.send(i / onePercent); } } var elapsedTime:int = getTimer() - startTime; var result:CountResult = new CountResult(targetValue, elapsedTime / 1000); resultChannel.send(result); trace("counted to", targetValue.toString(), "in", elapsedTime, "milliseconds"); } } }
registerClassAlias()
方法。
package com.adobe.example.vo { public class CountResult { public function CountResult(countTarget:uint=0, countTime:Number=0) { this.countTarget = countTarget; this.countDurationSeconds = countTime; } public var countTarget:uint; public var countDurationSeconds:Number; } }
1. 由於無法以動態方式載入包含 ActionScript 程式碼的遠端 SWF,因此,遠端 SWF 必須以移除的 SWF 形式傳遞給 Worker。2. 在 iOS 上無法使用 [Embed] 標記內嵌 SWF (具有 ABC 程式碼)。每個額外的 Worker 都是透過個別 SWF 來建立。若要建立 Worker 類別的新實體,請以背景 Worker 的 SWF 位元組做為引數,將 ByteArray 傳遞至 WorkerDomain 類別的 createWorker()
方法。
若要在 iOS 達到此目的,有兩個常見方式可以存取 SWF 的位元組:第一個方式是使用 Loader
載入外部 SWF 檔案,第二個方式是使用 URLLoader
載入 SWF 檔案。
下列範例使用 Loader
API 載入 SWF 檔案。
package { import flash.display.Loader; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.IOErrorEvent; import flash.net.URLRequest; import flash.system.ApplicationDomain; import flash.system.LoaderContext; import flash.system.MessageChannel; import flash.system.Worker; import flash.system.WorkerDomain; import flash.text.TextField; import flash.text.TextFormat; public class IOSWorkerExample extends Sprite { public var worker:Worker; public var bm:MessageChannel; public var mb:MessageChannel; public var tf:TextField; public var tfrmt:TextFormat; public function IOSWorkerExample() { super(); stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; tf=new TextField(); tfrmt= new TextFormat() tfrmt.size=80; tf.textColor = 0xFFFFF; tf.defaultTextFormat=tfrmt; addChild(tf); //creating the urlRequest object that references the background worker. var _urlRequest:URLRequest = new URLRequest("IOSBackWorker.swf"); var _loader:Loader = new Loader(); var _lc:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain, null); _loader.contentLoaderInfo.addEventListener(Event.COMPLETE,completeHandler); _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,errorHandler); _loader.load(_urlRequest, _lc); } // This function is called once the swf loading is completed public function completeHandler(e:Event):void { worker = WorkerDomain.current.createWorker(e.target.bytes); bm = worker.createMessageChannel(Worker.current); mb = Worker.current.createMessageChannel(worker); worker.setSharedProperty("btm", bm); worker.setSharedProperty("mtb", mb); //adding event handler on message receive from background bm.addEventListener(Event.CHANNEL_MESSAGE, onBackToMain); worker.start(); bm.receive(true); } public function errorHandler(e:IOErrorEvent):void { trace("In IO ErrorEvent Handler "+e.text); } //This function is called when the main thread receives the message from the background worker. public function onBackToMain(event:Event):void { if(bm.messageAvailable) { // displaying the percentages based on the message received from the background. var progress:Number = bm.receive(); trace("progress "+progress); tf.text= progress.toString(); } } } }
Loader
的載入函數,我們仍必須在背景 Worker 中放置 isPrimordial
屬性檢查,如這個範例所示。
package { import flash.display.Sprite; import flash.system.MessageChannel; import flash.system.Worker; import flash.utils.ByteArray; import flash.utils.getTimer; public class IOSBackWorker extends Sprite { private var memory:ByteArray = new ByteArray(); private var bm:MessageChannel; private var mb:MessageChannel; public function IOSBackWorker() { if(!Worker.current.isPrimordial) { memory.shareable = true; // creating objects of message channels bm = Worker.current.getSharedProperty("btm"); mb = Worker.current.getSharedProperty("mtb"); // calculating the percentage trace("message came"); var j:Number =1; while (j<1000) { for(var i=0;i<100;i++){} var startTime=getTimer(); // adding delay while (getTimer()-startTime <100); trace(j, (100*j)/1000); var progress:Number=(100*j)/1000; // sending the percentage to the main thread bm.send(progress); j++; } } } } }
Tue Jun 12 2018, 03:47 PM Z