當使用小組件工具箱時,務必瞭解用於讀取及分派平台 GUI 事件的基礎執行緒模型。 UI 執行緒的實作會影響在應用程式碼中使用 Java 執行緒時應用程式必須遵循 的規則。
在任何 GUI 應用程式下,無論是它的語言或 UI 工具箱,OS 平台會偵測 GUI 事件並放 置到應用程式事件佇列中。雖然不同 OS 平上的機制稍有不同,但基礎觀念都類似。 當使用者按一下滑鼠、輸入字元或在視窗表面工作,OS 會產生應用程式 GUI 事件, 例如點選、按鍵或視窗繪製事件。它判定哪一個視窗和應用程式應接收每一個事件, 並將該事件放置到應用程式的事件佇列中。
任何一般視窗 GUI 應用程式的基礎結構為事件迴圈。應用程式初值設定,然後啟動 僅讀取佇列中的 GUI 事件並相應地做出回應的迴圈。任何工作必須在處理這其中一個事件 的同時快速完成,使 GUI 系統繼續對使用者作出回應。
UI 事件所觸發的長時間作業應在個別的執行緒中執行,以便事件迴圈執行緒快速傳回及 從應用程式佇列中提取下一個事件。不過,其他執行緒中小組件和平台 API 的存取 權必須以明確的鎖定和序列化控制。無法遵循規則的應用程式會導致 OS 呼叫失敗, 更糟的是會鎖定整個 GUI 系統。
SWT 遵循直接由平台支援的執行緒作業模型。應用程式在其主要執行緒中執行事件 迴圈並直接從這個執行緒分派事件。UI 執行緒是建立 Display 所在的執行緒。所有其他小組件都必須在 UI 執行緒中建立。
因為所有事件碼是從應用程式的 UI 執行緒觸發,所以處理事件的應用程式碼可任意 存取小組件及呼叫圖形,而不需要任何特殊技術。不過,應用程式在執行回應事件的 長時間作業時負責分出計算執行緒。
附註:SWT 會對必須從 UI 執行緒執行但卻從非 UI 執行緒執行的任何呼叫, 觸發 SWTException。
SWT 應用程式的 main 執行緒(包括事件迴圈)有下列結構:
public static void main (String [] args) {Display display = new Display ();Shell shell = new Shell (display); shell.open (); // 開始事件迴圈。當使用者完成某事而 // 除去我們的視窗時即停止迴圈。 while (!shell.isDisposed ()) { if (!display.readAndDispatch()) display.sleep(); } display.dispose (); }
一旦建立小組件並開啟 Shell 後,應用程式會從 OS 佇列讀取及分派事件,直到除去 Shell 視窗為止。如果佇列中沒有適用的事件,則會通知 display 暫停,讓其他 應用程式有機會執行。
SWT 提供從背景緒呼叫小組件和圖形的特殊存取方法。
想要從非 UI 執行緒呼叫 UI 程式碼的應用程式必須提供呼叫 UI 程式碼的 Runnable。Display 類別中的 syncExec(Runnable) 和 asyncExec(Runnable) 方法, 用於在事件迴圈期間,在 UI 執行緒執行這些 Runnable。
下列程式碼片段示範使用這些方法的型樣:
// 執行耗時的計算 ... // 現在更新 UI。我們不依賴結果。 // 因此使用非同步。 display.asyncExec (new Runnable () { public void run () { if (!myWindow.isDisposed()) myWindow.redraw (); } }); // 現在做更多計算 ...
檢查在使用 asyncExec 時,是否在 Runnable 內除去您的小組件,是很好的做法。因為在呼叫 asyncExec
和執行 Runnable 之間的 UI 執行緒中,可發生其他事物,因此,永遠無法確定當執行 Runnable 時,您的小組件處於何種狀態。
當您從頭開始實作 SWT 應用程式時,執行緒作業規則就非常明確, 因為您控制事件迴圈的建立和在應用程式中分出計算執行緒的決策。
提供外掛程式程式碼給工作台時,事物會變得更為複雜一點。使用平台 UI 類別時,下列規則會被視為「使用規則」,雖然隨著版本的發行,有些規則可能會有變動: