第三章 分析 PBP Java AWT
3.2 Java AWT in PBP
Java AWT 有主要的幾個部分,抽象視窗元件的實作、事件驅動機制、layout 管 理、input device 的資訊接收。由於 Java ME PBP 比標準的 Java SE 的 Java 執行環境 能力較低,在考慮能夠實現以上功能又具有跨平台性,將 Java AWT 做稍微的改變,
採用的是 lightweight AWT 以及由 LightweightDispatcher 來分派來自 native 層的事 件。 們在特定視窗平台的實作。AWT 不直接和作業系統中的視窗工具(native GUI)組溝 通。它們會創造一個對等物件做為 AWT class 和原生(native)視窗工具組溝通時的管 道。每一個 class 的實體物件都會關連到相對應的對等 class(peer class)。若要將 Java API 移植到另一個工具組上,只有 peer interface 必須重新實作。較高的抽象類別:
如 Component、Button 都不用改變。這樣的 Java AWT 在移植時需要實作大量與平 台相關的 peer interface,稱為 heavyweight,不具有跨平台性又耗費系統資源。無法 跨平台的特性在嵌入式系統中就失去優勢。因此為了 Java AWT 更有跨台性與彈性,
在 PBP 中,大部分的元件不需要與平台相關的元件(peer class) 實作出來。除了部 分最底層的功能(ex. Window)才需要平台支援,稱作 lightweight。這樣的好處是與 平台的相依性降低了,處理最少與平台相關的程式問題,增加移植的簡單度,又可 以任意的改變元件外觀,不受限於每個平台。
Delegation event model(委託事件模型)
在 Java 中,將產生事件的設計與處理事件的動作獨立分開,稱為 Delegation event model。Java AWT 是一個事件驅動機制(Event-Driven Mechanism)的系統,在執行過 程中,設計根據不同的情形下會產生事件,然後藉著事件會去驅動相關動作的產生,
如果按照一般的程式設計邏輯,當發生事件時,需要去追蹤此事件屬於那一個 Component,之後還要尋找應該執行的 function,然後呼叫此 function 執行動作。所 以只要產生一個事件,就必須要去追蹤的話就太耗費資源了,為了節省追蹤的工作,
才研發出 Delegation event model。將產生事件的動作邏輯設計,與執行事件動作的 邏輯設計分開,中間委託 EventQueue 和 EventDispatchThread 去負責完成這連結的 動作,不僅產生的事件能觸發正確的動作還能節省資源,這是一個很好的設計,所 以 Delegation event model 會保留在簡化的設計中。
Event-Driven Mechanism (事件驅動機制)
Java AWT 的事件驅動機制就是一個程式的執行流程決定於事件,這個機制有 三大部分:一為事件(由 Event class 實作),根據來源不同大致可分為三類,有鍵盤 (key)事件、滑鼠(mouse)事件、或是繪圖元件(Component)事件;二為事件管理與分 派,由 EventQueue 負責排程規劃,EeventDispatchThread 負責分派;三為事件的接 收者(由 Listener class 實作),Listener 是在繪圖元件上負責過濾 event 的機制,唯有 註冊過的 event,才能被 Listener 接受並得到執行。事件驅動機制概念如圖 4:
圖 4 Java 事件驅動機制概念
當使用者輸入文字時,這時就會丟出鍵盤(key)事件的時候;當使用者滑鼠點擊 到繪圖元件時,這時就會丟出滑鼠(mouse)事件;當畫面需要重新更新時,Component 就會丟出 PaintEvent,或是視窗要縮小時 Component 會丟出 WindowEvent,不管是 哪一種方式產生的事件,都會交由系統統一管理與分派。系統的工具列(由 Toolkit Class 實作)中有一個負責接收所有事件並進行排程的 EventQueue,接著在固定的一 段時間內就會由 EeventDispatchThread 負責 pump 出事件,然後再根據事件的產生 來源分派給相對應的 Component,而 Component 上的事件接收者 Listener 決定是否 這事件有被註冊過,如果有就接受下來,接著 Component 就執行該事件的內容。這 樣就完成了一個事件被觸發後直到完成動作的流程。至於判斷是否有註冊過的方法 是 Java.awt.event 這個 package 裡已有定義好每個事件都有相對應的 Listener。如果 Component 中有宣告該事件相對應的 Listener,則表示此事件已被註冊。
分派來自 native 層的事件
在事件驅動機制中提到的 EeventDispatchThread 分派的事件是 Java event class,
不包括來自 native 層的事件。分派來自底 native 層的事件則會因為不同的 Java 平 台有不同的分派方式。在 Java SE 中的 AWT,來自 native 層的事件,例如鍵盤事件、
滑鼠事件,分析並把事件分派給屬於的 Component 這個工作,其實是交由 native GUI 負責。每一個 Component 在 native 層都有一個相對應的 peer 元件,負責記錄 Component 資訊,所以 native GUI 擁有 Component 的絕對位置座標,根據絕對位置 座標判斷完事件是屬於哪一個 Component,就產生一個 native 事件給 peer 元件,
透過 peer 元件在 native 層事件就直接分派給 Component 了。但在 Java ME PBP 中 除了 root container 以外,其他 Component 都是 lightweight,在 native 層並沒有相對 應的 peer 元件。native 層一點都不知道任何 Component 的資訊,必須將 native 層產 生的事件必需包裝成 Java event object,然後傳遞給 Java 層,分派事件的工作就交 由「輕量級分派者」負責(由 LightweightDispatcher Class 實作,為 Windows Class 持有)。從 native 層傳遞上來的 Java event object 都統一設定來源屬於 Window,然 後丟到 EventQueue 中進行排程與分派。Window 接受被分派來的 Java Event object,
先 交 由 LightweightDispatcher 。 LightweightDispatcher 分 析 事 件 座 標 位 置 跟 Component 定義的矩形範圍是否有交集,如果有的話,即判斷屬於該 Component,
則將 event 分派到該 Component。LightweightDispatcher 分派完如果事件沒被分派出 去,表示為 Window 的事件,則由 Window 進行事件的處理。