1. 程式人生 > >與swing的親密接觸(一) swingworker

與swing的親密接觸(一) swingworker

某些人一拍腦袋的事,讓我有機會跟Swing幹上了,因為專案組沒人用這玩意整過這東西,那就只能我硬著頭皮上了!
有時候人是要有點壓力才行的。

第一階段的開發
準備階段:工具的選擇。用了Netbeans 6,試用了下,感覺畫介面還比較容易,但是生成的程式碼很長,剛開始對Swing很陌生 ,看著netbeans 生成程式碼很頭痛,感覺不是我想要的東西,然後放棄了。然後是VE,因為也只是倒騰了下,沒細看。最後選擇了Jigloo ,但是用了之後才知道他生成的程式碼也很惡劣......
開發階段:別熟悉swing 邊開發,其中遇到亂七八遭事情一堆,但是因為這個軟體的功能是比較簡單的,最後終於是拿了出來,雖然BUG一堆,但是也算是我的第一swing作品,客戶感覺效果很不理想(有軟體設計方面的,也有技術運用方面的),也就有了下面繼續開發的經歷了。
主要技術方面問題是,執行緒的亂用造成死鎖,經常造成莫名的假死。

第二階段的開發:
因為第一階段的開發比較痛苦,所以決定換個語言開發。因為最近RIA比較熱,RIA裡面的adoble的air也算比較熱門的,UI給人感覺很華麗,也自己嘗試做過一些DEMO。專案中用的webservice,加密解密操作,檔案上傳下載都有解決的辦法,但是要命的是這個專案中要呼叫外部程式,air在這方面比較脆弱,google了一把 as 的 fscommand 能呼叫其他程式,但是 air 竟然不支援這個,後來還是塌塌實實用SWING 吧。
然後又試用了一把VE,感覺現在比上次我用的時候好多了,後來就把Jigloo換 VE了。
擺在眼前的就是如何運用好執行緒了。又google了一把,找到了swingworker 這個東西拉。從Java SE 6開始引進的SwingWorker能幫你輕鬆的編寫多執行緒Swing程式,改善你Swing程式的結構,提高介面響應的靈活性,這正是我要的東西。
一個Swing程式中一般有下面三種類型的執行緒:
* 初始化執行緒(Initial Thread)
* UI事件排程執行緒(EDT)
* 任務執行緒(Worker Thread)
Swing程式只有一個用EDT,該執行緒負責GUI元件的繪製和更新,通過呼叫程式的事件處理器來響應使用者互動。所有事件處理都是在EDT上進行的,程式同UI元件和其基本資料模型的互動只允許在EDT上進行,所有執行在EDT上的任務應該儘快完成,以便UI能及時響應使用者輸入。

Swing程式設計時應該注意以下幾點:
1.從其他執行緒訪問UI元件及其事件處理器會導致介面更新和繪製錯誤。
2.在EDT上執行耗時任務會使程式失去響應,這會使GUI事件阻塞在佇列中得不到處理。
3.應使用獨立的任務執行緒來執行耗時計算或輸入輸出密集型任務,比如同資料庫通訊、訪問網站資源、讀寫大樹據量的檔案。
而我第一階段開發的正是由於沒有注意到這點導致整個程式效果不佳。
程式中有個事件處理都要訪問Web服務,這些服務通常要許多秒後才能響應,在此期間,如果程式在EDT上進行Web服務互動,使用者就不能取消搜尋或者同介面互動,像這兩種都不應該在EDT上執行。
[img]http://www.iteye.com/upload/picture/pic/25513/c5ba1050-2586-3b73-9f16-b8e990bda961.bmp?1227186146[/img]
[img]http://pope945.iteye.com/upload/picture/pic/25515/ca107bbb-38b4-3b42-b5bb-14f4de6cf710.bmp[/img]

圖1顯示了在A和B點之間,EDT不能處理UI事件,AB兩點之間代表了程式訪Web服務的IO操作時間:
javax.swing.SwingWorker類是Java SE 6中新出現的類,使用SwingWorker,程式能啟動一個任務執行緒來非同步查詢,並馬上返回EDT執行緒。圖2顯示了使用SwingWorker後,事件處理立即返回,允許EDT繼續執行後續的UI事件。
原先就是都放在EDT上了,效果勉強也就難免了。而使用Swingworker啟動一個任務執行緒就可以靈活響應介面。
下面講講他的用法:
SwingWorker的定義如下:

public abstract class SwingWorker<T,V> extends Object implements RunnableFuture

SwingWorker是抽象類,因此必須繼承它才能執行所需的特定任務。注意該類有兩個型別引數:T及V。T是doInBackground和get方法的返回型別,V是publish和process方法要處理的資料型別。

SwingWorker實現以下介面方法:

* boolean cancel(boolean mayInterruptIfRunning)
* T get()
* T get(long timeout, TimeUnit unit)
* boolean isCancelled()
* boolean isDone()

SwingWorker實現了所有的介面方法,實際上你僅需要實現以下SwingWorker的抽象方法:

protected T doInBackground() throws Exception

doInBackground方法作為任務執行緒的一部分執行,它負責完成執行緒的基本任務,並以返回值來作為執行緒的執行結果。繼承類須覆蓋該方法並確保包含或代理任務執行緒的基本任務。不要直接呼叫該方法,應使用任務物件的execute方法來排程執行。

在獲得執行結果後應使用SwingWorker的get方法獲取doInBackground方法的結果。可以在EDT上呼叫get方法,但該方法將一直處於阻塞狀態,直到任務執行緒完成。最好只有在知道結果時才呼叫get方法,這樣使用者便不用等待。為防止阻塞,可以使用isDone方法來檢驗doInBackground是否完成。另外呼叫方法get(long timeout, TimeUnit unit)將會一直阻塞直到任務執行緒結束或超時。獲取任務結果的最好地方是在done方法內:

protected void done()

在doInBackground方法完成之後,SwingWorker呼叫done方法。如果任務需要在完成後使用執行緒結果更新GUI元件或者做些清理工作,可覆蓋done方法來完成它們。這兒是呼叫get方法的最好地方,因為此時已知道執行緒任務完成了,SwingWorker在EDT上啟用done方法,因此可以在此方法內安全地和任何GUI元件互動。

沒必要等到執行緒完成就可以獲得中間結果。中間結果是任務執行緒在產生最後結果之前就能產生的資料。當任務執行緒執行時,它可以釋出型別為V的中間結果,覆蓋process方法來處理中間結果。後文還將提供這些方法的更多詳細資訊。當屬性改變時,SwingWorker例項能通知處理器,SwingWorker有兩個重要的屬性:狀態和程序。任務執行緒有幾種狀態,以下面SwingWorker.StateValue列舉值來表示:

* PENDING
* STARTED
* DONE

任務執行緒一建立就處於PENDING狀態,當doInBackground方法開始時,任務執行緒就進入STARTED狀態,當doInBackground方法完成後,任務執行緒就處於DONE狀態,隨著執行緒進入各個階段,SwingWorker超類自動設定這些狀態值。你可以新增處理器,當這些屬性發生變化來接收通知。

最後,任務物件有一個進度屬性,隨著任務進展時,可以將這個屬性從0更新到100標識任務進度,當該屬性發生變化時,任務通知處理器進行處理。


我的使用感覺就是,象I/O操作,資料操作,網路操作等耗時的操作放到 doInBackground()中處理,任務執行中而非任務結束時釋出資料,要呼叫publish方法.
publish方法時,SwingWorker類排程process方法。有意思的是process方法是在EDT上面執行,這意味著可以同Swing元件和其模型直接互動。可以實現你在處理任務時,給個進度條提示。相關例子 [url]http://gceclub.sun.com.cn/Java_Docs/jdk6/html/zh_CN/api/javax/swing/SwingWorker.html[/url] 中有.



近日JDK 1.6u10釋出,comsumer jre 的釋出,我對swing 開發桌面程式充滿信心