“Digging more into the EventFlow”的版本间的差异

来自Blueidea
跳转至: 导航搜索
結點
同步和異步
 
(未显示同一用户的25个中间版本)
第3行: 第3行:
  
 
== 構架 ==
 
== 構架 ==
[[Image:FlashPlatform_DEF_base.png]]
+
<center>[[Image:FlashPlatform_DEF_base.png]]</center><br/>
 
   ActionScript3单一事件处理模型基於W3C DOM3規範。
 
   ActionScript3单一事件处理模型基於W3C DOM3規範。
   簡單來說。AVM2底層將Flash的元素表示一個樹狀結構。複合DOM的規範。
+
   簡單來說。AVM2底層將Flash的元素表示一個樹狀結構。符合DOM的規範。
   AVM2的垃圾回收機制也是在此基礎上。參見參考資料中的DOM3核心
+
   AVM2的垃圾回收機制也是在此基礎上。參見參考資料中的FlashPlatform GC
   總體上ActionScript3的事件流設施分為以下兩個部份:
+
   總體上ActionScript3的事件流體系按照職責和權限劃分為以下兩個部份:
 
[[Image:FlashPlatform_Numbers1.jpg]]虛線左側的DOM區。這是AVM底層實現並封裝的黑盒,用戶不需要瞭解其中細節。如果您的確要瞭解。請看參考資料。<br/>
 
[[Image:FlashPlatform_Numbers1.jpg]]虛線左側的DOM區。這是AVM底層實現並封裝的黑盒,用戶不需要瞭解其中細節。如果您的確要瞭解。請看參考資料。<br/>
 
[[Image:FlashPlatform_Numbers2.jpg]]虛線右側的Custom區。包含事件流機制構成以及提供給用戶的接口。<br/><br/>
 
[[Image:FlashPlatform_Numbers2.jpg]]虛線右側的Custom區。包含事件流機制構成以及提供給用戶的接口。<br/><br/>
 
DOM區提供一個接入點。用於支持事件流機制以訪問當前對象的樹模型。<br/>
 
DOM區提供一個接入點。用於支持事件流機制以訪問當前對象的樹模型。<br/>
 
用戶不需要知道事件流機制的實現細節也無法修改這個機制,因此只需要提供出派發和接收事件的支持。
 
用戶不需要知道事件流機制的實現細節也無法修改這個機制,因此只需要提供出派發和接收事件的支持。
 +
=== 參考資料 ===
 +
[[http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-249F15BA DOM3核心]]<br/>
 +
[[FlashPlatform GC]]
  
 
== 結點 ==
 
== 結點 ==
第17行: 第20行:
 
<center>[[Image:FlashPlatform_EFA.jpg]]</center><br/>
 
<center>[[Image:FlashPlatform_EFA.jpg]]</center><br/>
 
實際上。這個圖解為顯示對象樹。而不是DOM樹。<br/>
 
實際上。這個圖解為顯示對象樹。而不是DOM樹。<br/>
以下根據良種不同的對象事件傳遞的路徑來區分二者的差異:<br><br>
+
以下根據兩種不同的對象事件傳遞的路徑來區分二者的差異:<br><br>
  
 
對於非顯示對象而言。在DOM中不存在任何的父節點。因此當一個非顯示對象節點派發事件。那麼這個事件會立即回溯<br/>
 
對於非顯示對象而言。在DOM中不存在任何的父節點。因此當一個非顯示對象節點派發事件。那麼這個事件會立即回溯<br/>
 
<center>[[Image:FlashPlatform_NormalObjectNode.png]]</center><br/>
 
<center>[[Image:FlashPlatform_NormalObjectNode.png]]</center><br/>
  
對於顯示對象。當沒有被添加到顯示列表時和非顯示對象派發事件沒有任何區別。
+
對於顯示對象。當沒有被添加到顯示列表時和非顯示對象沒有任何區別。而在顯示列表中時才會像下圖這樣。
 +
<center>[[Image:FlashPlatform_DisplayObjectNode.png]]</center><br/>
 +
根據DOM的定義。並沒有特別對於顯示成員stage的描述。雖然事件流機制被描述為事件首先會經過根節點stage。<br/>
 +
所以實際上這個圖的情況。也存在完整的事件流階段。一個事件最開始經歷的並不一定是stage<br/>
 +
而是從事件派發的目標對象在DOM中的定義,向上遍歷父節點直到根節點。再根據這個DOM的根節點印射出實際對象。<br/>
 +
因此以下代碼運行可以發現兩個監聽函數都運行了。<br/>
 +
<syntaxhighlight lang="actionscript">
 +
package {
 +
  import flash.display.Sprite;
 +
  import flash.events.Event;
  
==同步和異步==
+
  public class EventFlowTest extends Sprite {
在程序的設計上是異步的。具體實現是同步。待編輯條目。
+
    //==========================================================================
 +
    //  Constructor
 +
    //==========================================================================
 +
    /** Constructor */
 +
    public function EventFlowTest() {
 +
      const A:Sprite = new Sprite();
 +
      const B:Sprite = new Sprite();
 +
      A.addChild(B);
 +
     
 +
      A.addEventListener("myEvent", A_listener, true);
 +
      B.addEventListener("myEvent", B_listener);
 +
      B.dispatchEvent(new Event("myEvent"));
 +
    }
 +
    //==========================================================================
 +
    //  Event listeners
 +
    //==========================================================================
 +
    private function A_listener(event:Event):void {
 +
      trace("A_listener");
 +
    }
 +
    private function B_listener(event:Event):void {
 +
      trace("B_listener");
 +
    }
 +
  } // <- end class ->
 +
}
 +
</syntaxhighlight>
 +
'''事件流機制的工作中。'''<br/>
 +
  在DOM查找父級節需要滿足"顯示包含關係",以下圖中紅色視線表示。<br/>
 +
  而非顯示包含關係比如一個繼承Sprite的類的實例a有成員變量b,這個a和b的"引用關係"就是用灰色虛線所表示。<br/>
 +
假設顯示列表的結構為下圖所示<br/>
 +
<center>[[Image:FlashPlatform_DOMSync.png]]</center><br/>
 +
DOM中的對象樹。和父節點間為紅色實線的節點可以向上繼續遍歷。
 +
<center>[[Image:FlashPlatform_DOMDetail.png]]</center><br/>
 +
這就解釋了為甚麼一個new EventListener()派發出的事件只有這個對象自己可以接收。<br/>
 +
  注所有ActionScript對象會在DOM區建立一個與之對應的定義,不管是顯示對象也好非顯示對象也好並沒有區別。
 +
  圖中的DOM結構用來闡述原理。實際DOM底層結構並不一定如圖所示。也有可能是以別的方式來組織。
  
==事件流體系結構==
+
==監聽器==
 +
與DOM定義不同的是。在ActionScript3中。<br/>
 +
監聽一個事件。並不是使用一個實現DOM的EventListener接口的對象實例。當然就不需要實現handleEvent方法。<br/>
 +
使用ActionScript的Function監聽就可以了。這的確要比寫一個類要簡單。<br/>
 +
如果ActionScript2的玩家新學ActionScript3,突然發現又要addEventListener又要寫EventListener類還要再實現接口。應該會很囧吧。<br/>
  
 +
筆者推測。實際上DOM的handleEvent方法還是實現了的。在事件流機制中。<br/>
 +
可以動態的在執行時封裝一個實現DOM的EventListener對象並把用戶函數定義在其中<br/>
 +
這樣DOM的事件機制傳遞結果給事件流機制的EventListener。再由事件流機制充當中間層將事件經過定義再EventListener中的用戶函數轉發給用戶。<br/>
 +
<br/>
 
=== 參考資料 ===
 
=== 參考資料 ===
[[http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-249F15BA DOM3核心]]<br/>
+
[[http://livedocs.adobe.com/flash/9.0_cn/main/flash_as3_programming.pdf Flash_AS3_Programming.PDF]]<br/>
[[FlashPlatform GC]]
+
 
 +
==同步和異步==
 +
ActionScript3的事件機制是同步執行的。
 +
AVM2並沒有支持多線程,因此控件或者是別的對象派發事件都只能走一個線程。
 +
因此如果在一個事件派發的代碼行之前有大量計算。就會導致這個事件晚很多才被派發出去。監聽這個事件的單位也會很晚才做出反映。
 +
所以會卡。假設一個應用。邏輯計算走一個線程。控件的事件和更新走另一個線程。那麼就算執行大量計算。也不至於整個程序卡死。
 +
操作系統工作再忙碌。活動監視器也還是可以調出來的嘛。
 +
 
 +
這個範例可以說明事件並不是異步的。
 +
<syntaxhighlight lang="actionscript">
 +
package {
 +
  import flash.display.Sprite;
 +
  import flash.events.Event;
 +
 
 +
  public class EventFlowSyncTest extends Sprite {
 +
    //==========================================================================
 +
    //  Constructor
 +
    //==========================================================================
 +
    /** Constructor */
 +
    public function EventFlowSyncTest() {
 +
      const S:Sprite = new Sprite();
 +
      S.addEventListener("myEvent", listener);
 +
      trace("before dispatch");
 +
      S.dispatchEvent(new Event("myEvent"));
 +
      trace("after dispatch");
 +
    }
 +
    //==========================================================================
 +
    //  Event listeners
 +
    //==========================================================================
 +
    private function listener(event:Event):void {
 +
      trace("capture");
 +
    }
 +
  } // <- end class ->
 +
}
 +
</syntaxhighlight>
 +
你會得到以下輸出,如果事件是異步的。它應該晚於trace("after dispatch");的執行。
 +
  before dispatch
 +
  capture
 +
  after dispatch
 +
 
 +
 
 +
 
 +
異步在編程中可以理解為延遲的一種調用。<br>
 +
多線程就會造成這個效果。但並不代表單線程沒有異步的概念。<br>
 +
本質上來說。異步是一種概念,代表不需要立即做出反應。<br>
 +
最常見的例子就是在應用中加載一張圖片。我們沒必要等在那裡等加載完再繼續做別的事。<br>
 +
系統中內置很多事件類型。都是由系統派發的。而不是用戶手工dispatchEvent的。<br>
 +
  我們不知道一個事件何時何地將會派發出來。因此才把一件事情看作是異步的。<br>
 +
  雖然自定義事件是我們自己拋出。我們知道它何時何地派發出來。但是在處理這個事件上,我們還是應該當成我們並不知道這個事件是從哪來的。<br>
 +
  這樣我們才能區分開了"通告做一件事情" 和 "親自去做一件事情"。
 +
 
 +
 
 +
===紕漏和修正===
 +
筆者對於細節的描述出於個人理解而非官方闡述。因此不免有紕漏指出。
 +
如果讀者發現其中有問題的地方還請告知。筆者會在第一時間進行修正。對於您的糾錯和改進意見筆者非常感激。
 +
筆者信箱:purple.starpulse@gmail.com
 +
 
 +
您也可以直接修改以上內容。

2011-04-17T08:26:05的最后版本

深入挖掘ActionScript3事件流的原理和細節。

構架

FlashPlatform DEF base.png

 ActionScript3单一事件处理模型基於W3C DOM3規範。
 簡單來說。AVM2底層將Flash的元素表示一個樹狀結構。符合DOM的規範。
 AVM2的垃圾回收機制也是在此基礎上。參見參考資料中的FlashPlatform GC
 總體上ActionScript3的事件流體系按照職責和權限劃分為以下兩個部份:

FlashPlatform Numbers1.jpg虛線左側的DOM區。這是AVM底層實現並封裝的黑盒,用戶不需要瞭解其中細節。如果您的確要瞭解。請看參考資料。
FlashPlatform Numbers2.jpg虛線右側的Custom區。包含事件流機制構成以及提供給用戶的接口。

DOM區提供一個接入點。用於支持事件流機制以訪問當前對象的樹模型。
用戶不需要知道事件流機制的實現細節也無法修改這個機制,因此只需要提供出派發和接收事件的支持。

參考資料

[DOM3核心]
FlashPlatform GC

結點

事件流機制通常描述為一個三階段的流程。並以stage為根節點出發。

FlashPlatform EFA.jpg

實際上。這個圖解為顯示對象樹。而不是DOM樹。
以下根據兩種不同的對象事件傳遞的路徑來區分二者的差異:

對於非顯示對象而言。在DOM中不存在任何的父節點。因此當一個非顯示對象節點派發事件。那麼這個事件會立即回溯

FlashPlatform NormalObjectNode.png

對於顯示對象。當沒有被添加到顯示列表時和非顯示對象沒有任何區別。而在顯示列表中時才會像下圖這樣。

FlashPlatform DisplayObjectNode.png

根據DOM的定義。並沒有特別對於顯示成員stage的描述。雖然事件流機制被描述為事件首先會經過根節點stage。
所以實際上這個圖的情況。也存在完整的事件流階段。一個事件最開始經歷的並不一定是stage
而是從事件派發的目標對象在DOM中的定義,向上遍歷父節點直到根節點。再根據這個DOM的根節點印射出實際對象。
因此以下代碼運行可以發現兩個監聽函數都運行了。

package {
  import flash.display.Sprite;
  import flash.events.Event;
 
  public class EventFlowTest extends Sprite {
    //==========================================================================
    //  Constructor
    //==========================================================================
    /** Constructor */
    public function EventFlowTest() {
      const A:Sprite = new Sprite();
      const B:Sprite = new Sprite();
      A.addChild(B);
 
      A.addEventListener("myEvent", A_listener, true);
      B.addEventListener("myEvent", B_listener);
      B.dispatchEvent(new Event("myEvent"));
    }
    //==========================================================================
    //  Event listeners
    //==========================================================================
    private function A_listener(event:Event):void {
      trace("A_listener");
    }
    private function B_listener(event:Event):void {
      trace("B_listener");
    }
  } // <- end class ->
}

事件流機制的工作中。

 在DOM查找父級節需要滿足"顯示包含關係",以下圖中紅色視線表示。
而非顯示包含關係比如一個繼承Sprite的類的實例a有成員變量b,這個a和b的"引用關係"就是用灰色虛線所表示。

假設顯示列表的結構為下圖所示

FlashPlatform DOMSync.png

DOM中的對象樹。和父節點間為紅色實線的節點可以向上繼續遍歷。

FlashPlatform DOMDetail.png

這就解釋了為甚麼一個new EventListener()派發出的事件只有這個對象自己可以接收。

 注所有ActionScript對象會在DOM區建立一個與之對應的定義,不管是顯示對象也好非顯示對象也好並沒有區別。
 圖中的DOM結構用來闡述原理。實際DOM底層結構並不一定如圖所示。也有可能是以別的方式來組織。

監聽器

與DOM定義不同的是。在ActionScript3中。
監聽一個事件。並不是使用一個實現DOM的EventListener接口的對象實例。當然就不需要實現handleEvent方法。
使用ActionScript的Function監聽就可以了。這的確要比寫一個類要簡單。
如果ActionScript2的玩家新學ActionScript3,突然發現又要addEventListener又要寫EventListener類還要再實現接口。應該會很囧吧。

筆者推測。實際上DOM的handleEvent方法還是實現了的。在事件流機制中。
可以動態的在執行時封裝一個實現DOM的EventListener對象並把用戶函數定義在其中
這樣DOM的事件機制傳遞結果給事件流機制的EventListener。再由事件流機制充當中間層將事件經過定義再EventListener中的用戶函數轉發給用戶。

參考資料

[Flash_AS3_Programming.PDF]

同步和異步

ActionScript3的事件機制是同步執行的。 AVM2並沒有支持多線程,因此控件或者是別的對象派發事件都只能走一個線程。 因此如果在一個事件派發的代碼行之前有大量計算。就會導致這個事件晚很多才被派發出去。監聽這個事件的單位也會很晚才做出反映。 所以會卡。假設一個應用。邏輯計算走一個線程。控件的事件和更新走另一個線程。那麼就算執行大量計算。也不至於整個程序卡死。 操作系統工作再忙碌。活動監視器也還是可以調出來的嘛。

這個範例可以說明事件並不是異步的。

package {
  import flash.display.Sprite;
  import flash.events.Event;
 
  public class EventFlowSyncTest extends Sprite {
    //==========================================================================
    //  Constructor
    //==========================================================================
    /** Constructor */
    public function EventFlowSyncTest() {
      const S:Sprite = new Sprite();
      S.addEventListener("myEvent", listener);
      trace("before dispatch");
      S.dispatchEvent(new Event("myEvent"));
      trace("after dispatch");
    }
    //==========================================================================
    //  Event listeners
    //==========================================================================
    private function listener(event:Event):void {
      trace("capture");
    }
  } // <- end class ->
}

你會得到以下輸出,如果事件是異步的。它應該晚於trace("after dispatch");的執行。

 before dispatch
 capture
 after dispatch


異步在編程中可以理解為延遲的一種調用。
多線程就會造成這個效果。但並不代表單線程沒有異步的概念。
本質上來說。異步是一種概念,代表不需要立即做出反應。
最常見的例子就是在應用中加載一張圖片。我們沒必要等在那裡等加載完再繼續做別的事。
系統中內置很多事件類型。都是由系統派發的。而不是用戶手工dispatchEvent的。

 我們不知道一個事件何時何地將會派發出來。因此才把一件事情看作是異步的。
雖然自定義事件是我們自己拋出。我們知道它何時何地派發出來。但是在處理這個事件上,我們還是應該當成我們並不知道這個事件是從哪來的。
這樣我們才能區分開了"通告做一件事情" 和 "親自去做一件事情"。


紕漏和修正

筆者對於細節的描述出於個人理解而非官方闡述。因此不免有紕漏指出。 如果讀者發現其中有問題的地方還請告知。筆者會在第一時間進行修正。對於您的糾錯和改進意見筆者非常感激。 筆者信箱:purple.starpulse@gmail.com

您也可以直接修改以上內容。